2022-03-21 22:27:35 +00:00
package components
import (
2022-03-24 22:48:51 +00:00
"context"
"log"
2022-03-21 22:27:35 +00:00
"github.com/maxence-charriere/go-app/v9/pkg/app"
2022-03-23 04:06:56 +00:00
"github.com/mlctrez/goapp-mdc/pkg/bar"
2022-03-23 17:20:17 +00:00
"github.com/mlctrez/goapp-mdc/pkg/base"
2022-03-23 04:06:56 +00:00
"github.com/mlctrez/goapp-mdc/pkg/icon"
"github.com/mlctrez/goapp-mdc/pkg/textfield"
2022-03-24 22:48:51 +00:00
"go.mills.io/saltyim"
2022-03-28 21:49:01 +00:00
"go.mills.io/saltyim/internal/pwa/storage"
"go.yarn.social/lextwt"
2022-03-21 22:27:35 +00:00
)
2022-03-22 04:04:47 +00:00
const (
2022-03-28 21:49:01 +00:00
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. `
2022-03-22 04:04:47 +00:00
)
2022-03-28 21:49:01 +00:00
var client * saltyim . Client
2022-03-25 11:28:30 +00:00
// SaltyChat ...
type SaltyChat struct {
2022-03-21 22:27:35 +00:00
app . Compo
2022-03-23 17:20:17 +00:00
base . JsUtil
2022-03-23 04:06:56 +00:00
isAppInstallable bool
2022-03-24 22:48:51 +00:00
2022-03-28 21:49:01 +00:00
navigation * Navigation
dialog * ModalDialog
chatBox * ChatBox
2022-03-25 04:20:16 +00:00
2022-03-28 21:49:01 +00:00
Friend string
2022-03-24 22:48:51 +00:00
chatInput * textfield . TextField
incoming chan string
2022-03-22 04:43:05 +00:00
}
2022-03-25 11:28:30 +00:00
func ( h * SaltyChat ) init ( ctx app . Context ) {
2022-03-25 06:49:42 +00:00
ctx . Page ( ) . SetTitle ( "Salty Chat" )
2022-03-22 04:43:05 +00:00
}
2022-03-25 11:28:30 +00:00
func ( h * SaltyChat ) load ( ctx app . Context ) {
2022-03-22 04:48:54 +00:00
ctx . Page ( ) . SetTitle ( "Salty Chat" )
2022-03-22 04:43:05 +00:00
ctx . Page ( ) . SetDescription ( descText )
}
2022-03-25 11:28:30 +00:00
func ( h * SaltyChat ) OnPreRender ( ctx app . Context ) {
2022-03-22 04:43:05 +00:00
h . init ( ctx )
h . load ( ctx )
}
2022-03-25 11:28:30 +00:00
func ( h * SaltyChat ) OnResize ( ctx app . Context ) {
2022-03-22 04:43:05 +00:00
h . ResizeContent ( )
2022-03-28 21:49:01 +00:00
h . navigation . drawer . ActionClose ( ctx )
2022-03-22 04:04:47 +00:00
}
2022-03-25 11:28:30 +00:00
func ( h * SaltyChat ) OnAppInstallChange ( ctx app . Context ) {
2022-03-22 04:04:47 +00:00
h . isAppInstallable = ctx . IsAppInstallable ( )
}
2022-03-28 21:49:01 +00:00
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 )
}
2022-03-25 11:28:30 +00:00
func ( h * SaltyChat ) OnMount ( ctx app . Context ) {
2022-03-28 21:49:01 +00:00
2022-03-22 04:04:47 +00:00
h . isAppInstallable = ctx . IsAppInstallable ( )
2022-03-28 21:49:01 +00:00
h . refreshMessages ( ctx )
2022-03-24 22:48:51 +00:00
if app . IsClient {
h . connect ( ctx )
}
}
2022-03-25 11:28:30 +00:00
func ( h * SaltyChat ) connect ( ctx app . Context ) {
2022-03-28 21:49:01 +00:00
if client != nil {
2022-03-24 22:48:51 +00:00
return
}
identity , err := GetIdentityFromState ( ctx )
if err != nil {
2022-03-28 21:49:01 +00:00
h . dialog . ShowDialog ( "missing identity, please configure" , err . Error ( ) )
2022-03-24 22:48:51 +00:00
return
}
2022-03-28 21:49:01 +00:00
newClient , err := saltyim . NewClient ( identity . Addr ( ) , saltyim . WithIdentityBytes ( identity . Contents ( ) ) )
2022-03-24 22:48:51 +00:00
if err != nil {
2022-03-28 21:49:01 +00:00
h . dialog . ShowDialog ( "error setting up client" , err . Error ( ) )
2022-03-24 22:48:51 +00:00
return
}
2022-03-28 21:49:01 +00:00
client = newClient
2022-03-24 22:48:51 +00:00
2022-03-25 12:47:06 +00:00
ctx . Async ( func ( ) {
2022-03-28 21:49:01 +00:00
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
}
}
2022-03-24 22:48:51 +00:00
}
2022-03-25 12:47:06 +00:00
} )
2022-03-21 22:27:35 +00:00
}
2022-03-25 11:28:30 +00:00
func ( h * SaltyChat ) Render ( ) app . UI {
2022-03-28 21:49:01 +00:00
2022-03-23 04:06:56 +00:00
topBar := & bar . TopAppBar { Title : "Salty IM" ,
2022-03-28 21:49:01 +00:00
Navigation : [ ] app . HTMLButton { icon . MIMenu . Button ( ) . OnClick ( func ( ctx app . Context , e app . Event ) {
h . navigation . drawer . ActionOpen ( ctx )
} ) } ,
2022-03-23 17:08:21 +00:00
Fixed : true ,
ScrollTarget : "main-content" ,
Actions : h . topActions ( ) ,
2022-03-23 04:06:56 +00:00
}
2022-03-28 21:49:01 +00:00
if h . chatBox == nil {
h . chatBox = & ChatBox { }
2022-03-25 02:11:26 +00:00
h . chatInput = & textfield . TextField { Id : "chat-input" , Placeholder : ">" }
2022-03-28 21:49:01 +00:00
//h.friend = &textfield.TextField{Id: "friend-input", Placeholder: "send-to"}
h . dialog = & ModalDialog { }
h . navigation = & Navigation { }
2022-03-23 04:06:56 +00:00
}
2022-03-28 21:49:01 +00:00
h . chatBox . User = h . Friend
2022-03-23 17:08:21 +00:00
return app . Div ( ) . Body (
2022-03-28 21:49:01 +00:00
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 ,
2022-03-23 04:06:56 +00:00
) ,
2022-03-22 04:43:05 +00:00
) ,
2022-03-23 04:06:56 +00:00
) ,
2022-03-23 17:08:21 +00:00
) ,
)
2022-03-25 04:20:16 +00:00
}
2022-03-25 11:28:30 +00:00
func ( h * SaltyChat ) handleSendMessage ( ctx app . Context , e app . Event ) {
2022-03-25 04:20:16 +00:00
e . PreventDefault ( )
msg := h . chatInput . Value
h . chatInput . Value = ""
h . focusChatInput ( )
2022-03-28 21:49:01 +00:00
//friendAddress := strings.TrimSpace(h.friend.Value)
if msg == "" || h . Friend == "" {
2022-03-25 04:20:16 +00:00
// nothing to send
return
}
2022-03-28 21:49:01 +00:00
storage . ContactsLocalStorage ( ctx ) . Add ( h . Friend )
2022-03-25 04:20:16 +00:00
// determine current user to send message to and use client to send the message
2022-03-28 21:49:01 +00:00
if client != nil {
//h.friend.Value = friendAddress
h . chatBox . User = h . Friend
2022-03-29 00:24:03 +00:00
ctx . Async ( func ( ) {
2022-03-28 21:49:01 +00:00
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 )
2022-03-25 04:20:16 +00:00
} else {
2022-03-28 21:49:01 +00:00
h . dialog . ShowDialog ( "error sending message" , err . Error ( ) )
2022-03-25 04:20:16 +00:00
}
2022-03-29 00:24:03 +00:00
} )
2022-03-25 04:20:16 +00:00
}
}
2022-03-25 11:28:30 +00:00
func ( h * SaltyChat ) focusChatInput ( ) {
2022-03-24 22:48:51 +00:00
chatInputValue := h . JsUtil . JsValueAtPath ( h . chatInput . Id + "-input" )
chatInputValue . Set ( "value" , "" )
chatInputValue . Call ( "focus" )
}
2022-03-25 11:28:30 +00:00
func ( h * SaltyChat ) topActions ( ) ( actions [ ] app . HTMLButton ) {
2022-03-23 17:08:21 +00:00
if h . isAppInstallable {
actions = append ( actions , icon . MIDownload . Button ( ) . Title ( "Install PWA" ) .
OnClick ( func ( ctx app . Context , e app . Event ) { ctx . ShowAppInstallPrompt ( ) } ) )
}
2022-03-28 21:49:01 +00:00
actions = append ( actions , icon . MIRefresh . Button ( ) . Title ( "reload" ) .
2022-03-23 17:08:21 +00:00
OnClick ( func ( ctx app . Context , e app . Event ) { ctx . Reload ( ) } ) )
return actions
2022-03-22 04:04:47 +00:00
}