mirror of
https://git.mills.io/saltyim/saltyim.git
synced 2024-07-08 02:51:32 +00:00
![James Mills](/assets/img/avatar_default.png)
Co-authored-by: James Mills <prologic@shortcircuit.net.au> Reviewed-on: https://git.mills.io/saltyim/saltyim/pulls/84
221 lines
5.1 KiB
Go
221 lines
5.1 KiB
Go
package components
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
|
|
"github.com/maxence-charriere/go-app/v9/pkg/app"
|
|
"github.com/mlctrez/goapp-mdc/pkg/bar"
|
|
"github.com/mlctrez/goapp-mdc/pkg/base"
|
|
"github.com/mlctrez/goapp-mdc/pkg/icon"
|
|
"github.com/mlctrez/goapp-mdc/pkg/textfield"
|
|
"go.mills.io/saltyim"
|
|
"go.mills.io/saltyim/internal/pwa/storage"
|
|
"go.yarn.social/lextwt"
|
|
)
|
|
|
|
const (
|
|
descText = `salty.im is an open specification for a new Saltpack based e2e encrypted messaging protocol and platform for secure communications with a focus on privacy, security and being self-hosted.`
|
|
)
|
|
|
|
var client *saltyim.Client
|
|
|
|
// SaltyChat ...
|
|
type SaltyChat struct {
|
|
app.Compo
|
|
base.JsUtil
|
|
isAppInstallable bool
|
|
|
|
navigation *Navigation
|
|
dialog *ModalDialog
|
|
chatBox *ChatBox
|
|
|
|
Friend string
|
|
chatInput *textfield.TextField
|
|
|
|
incoming chan string
|
|
}
|
|
|
|
func (h *SaltyChat) init(ctx app.Context) {
|
|
ctx.Page().SetTitle("Salty Chat")
|
|
}
|
|
|
|
func (h *SaltyChat) load(ctx app.Context) {
|
|
ctx.Page().SetTitle("Salty Chat")
|
|
ctx.Page().SetDescription(descText)
|
|
}
|
|
|
|
func (h *SaltyChat) OnPreRender(ctx app.Context) {
|
|
h.init(ctx)
|
|
h.load(ctx)
|
|
}
|
|
|
|
func (h *SaltyChat) OnResize(ctx app.Context) {
|
|
h.ResizeContent()
|
|
h.navigation.drawer.ActionClose(ctx)
|
|
}
|
|
|
|
func (h *SaltyChat) OnAppInstallChange(ctx app.Context) {
|
|
h.isAppInstallable = ctx.IsAppInstallable()
|
|
}
|
|
|
|
func (h *SaltyChat) OnNav(ctx app.Context) {
|
|
h.refreshMessages(ctx)
|
|
}
|
|
|
|
func (h *SaltyChat) refreshMessages(ctx app.Context) {
|
|
|
|
if ctx.Page().URL().Fragment == "" {
|
|
return
|
|
}
|
|
|
|
h.Friend = ctx.Page().URL().Fragment
|
|
h.chatBox.User = h.Friend
|
|
h.chatBox.UpdateMessages(ctx)
|
|
|
|
}
|
|
|
|
func (h *SaltyChat) OnMount(ctx app.Context) {
|
|
|
|
h.isAppInstallable = ctx.IsAppInstallable()
|
|
|
|
h.refreshMessages(ctx)
|
|
if app.IsClient {
|
|
h.connect(ctx)
|
|
}
|
|
}
|
|
|
|
func (h *SaltyChat) connect(ctx app.Context) {
|
|
|
|
if client != nil {
|
|
return
|
|
}
|
|
|
|
identity, err := GetIdentityFromState(ctx)
|
|
if err != nil {
|
|
h.dialog.ShowDialog("missing identity, please configure", err.Error())
|
|
return
|
|
}
|
|
|
|
newClient, err := saltyim.NewClient(identity.Addr(), saltyim.WithIdentityBytes(identity.Contents()))
|
|
if err != nil {
|
|
h.dialog.ShowDialog("error setting up client", err.Error())
|
|
return
|
|
}
|
|
|
|
client = newClient
|
|
|
|
ctx.Async(func() {
|
|
for msg := range client.Drain(context.Background(), "", "", "") {
|
|
s, err := lextwt.ParseSalty(msg.Text)
|
|
if err != nil {
|
|
log.Println("incoming message error", err)
|
|
continue
|
|
}
|
|
switch s := s.(type) {
|
|
case *lextwt.SaltyText:
|
|
user := s.User.String()
|
|
storage.ConversationsLocalStorage(ctx, user).Append(msg.Text)
|
|
// only update when incoming user's message is the active chat
|
|
if h.Friend == user {
|
|
h.chatBox.UpdateMessages(ctx)
|
|
} else {
|
|
// TODO: how to notify message received in background
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func (h *SaltyChat) Render() app.UI {
|
|
|
|
topBar := &bar.TopAppBar{Title: "Salty IM",
|
|
Navigation: []app.HTMLButton{icon.MIMenu.Button().OnClick(func(ctx app.Context, e app.Event) {
|
|
h.navigation.drawer.ActionOpen(ctx)
|
|
})},
|
|
Fixed: true,
|
|
ScrollTarget: "main-content",
|
|
Actions: h.topActions(),
|
|
}
|
|
|
|
if h.chatBox == nil {
|
|
h.chatBox = &ChatBox{}
|
|
h.chatInput = &textfield.TextField{Id: "chat-input", Placeholder: ">"}
|
|
//h.friend = &textfield.TextField{Id: "friend-input", Placeholder: "send-to"}
|
|
h.dialog = &ModalDialog{}
|
|
h.navigation = &Navigation{}
|
|
}
|
|
|
|
h.chatBox.User = h.Friend
|
|
|
|
return app.Div().Body(
|
|
h.navigation,
|
|
app.Div().Class("mdc-drawer-app-content").Body(
|
|
&AppUpdateBanner{},
|
|
topBar,
|
|
app.Div().Class("main-content").ID("main-content").Body(
|
|
topBar.Main().Body(
|
|
app.Div().ID("wrapper").Body(
|
|
h.chatBox,
|
|
app.Form().OnSubmit(h.handleSendMessage).Body(
|
|
h.chatInput,
|
|
),
|
|
h.dialog,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
)
|
|
|
|
}
|
|
|
|
func (h *SaltyChat) handleSendMessage(ctx app.Context, e app.Event) {
|
|
e.PreventDefault()
|
|
msg := h.chatInput.Value
|
|
h.chatInput.Value = ""
|
|
h.focusChatInput()
|
|
|
|
//friendAddress := strings.TrimSpace(h.friend.Value)
|
|
|
|
if msg == "" || h.Friend == "" {
|
|
// nothing to send
|
|
return
|
|
}
|
|
|
|
storage.ContactsLocalStorage(ctx).Add(h.Friend)
|
|
|
|
// determine current user to send message to and use client to send the message
|
|
if client != nil {
|
|
//h.friend.Value = friendAddress
|
|
h.chatBox.User = h.Friend
|
|
ctx.Async(func() {
|
|
if err := client.Send(h.Friend, msg); err == nil {
|
|
storage.ConversationsLocalStorage(ctx, h.Friend).
|
|
Append(string(saltyim.PackMessage(client.Me(), msg)))
|
|
h.chatBox.UpdateMessages(ctx)
|
|
} else {
|
|
h.dialog.ShowDialog("error sending message", err.Error())
|
|
}
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func (h *SaltyChat) focusChatInput() {
|
|
chatInputValue := h.JsUtil.JsValueAtPath(h.chatInput.Id + "-input")
|
|
chatInputValue.Set("value", "")
|
|
chatInputValue.Call("focus")
|
|
}
|
|
|
|
func (h *SaltyChat) topActions() (actions []app.HTMLButton) {
|
|
if h.isAppInstallable {
|
|
actions = append(actions, icon.MIDownload.Button().Title("Install PWA").
|
|
OnClick(func(ctx app.Context, e app.Event) { ctx.ShowAppInstallPrompt() }))
|
|
}
|
|
|
|
actions = append(actions, icon.MIRefresh.Button().Title("reload").
|
|
OnClick(func(ctx app.Context, e app.Event) { ctx.Reload() }))
|
|
|
|
return actions
|
|
}
|