6
1
mirror of https://git.mills.io/saltyim/saltyim.git synced 2024-06-16 11:58:24 +00:00

Add support for new contacts appearing in contact list automatically (#89)

Co-authored-by: James Mills <prologic@shortcircuit.net.au>
Co-authored-by: mlctrez <mlctrez@gmail.com>
Reviewed-on: https://git.mills.io/saltyim/saltyim/pulls/89
This commit is contained in:
James Mills 2022-03-29 22:06:06 +00:00
parent 7fc5e7184b
commit f9fa628e75
8 changed files with 98 additions and 19 deletions

@ -30,14 +30,19 @@ preflight: ## Run preflight checks to ensure you have the right build tools
@./preflight.sh
deps: ## Install any dependencies required
@$(GOCMD) install github.com/jsha/minica@latest
@$(GOCMD) install git.mills.io/prologic/devdns@latest
dev : DEBUG=1
dev : build ## Build debug versions of the cli and server
@./salty-chat -v
@./saltyd -v
certs: certs/minica-key.pem certs/minica.pem certs/salty.home.arpa/key.pem certs/salty.home.arpa/cert.pem
@/bin/sh -c 'cd certs && minica --domains salty.home.arpa'
pwa-dev : DEBUG=1
pwa-dev : build ## Build debug version of saltyd and PWA
pwa-dev : build certs ## Build debug version of saltyd and PWA
@CGO_ENABLED=1 $(GOCMD) build -tags "embed" ./cmd/saltyd/...
@./saltyd -D -b :https -u https://salty.home.arpa \
--tls --tls-key ./certs/salty.home.arpa/key.pem \

@ -306,6 +306,13 @@ func (cli *Client) Subscribe(ctx context.Context, extraenvs, prehook, posthook s
return msgs
}
// Lookup performs a lookup for a user's address and config
// If the user has an address already cached, the cached addr
// is returned, otherwise a full lookup is done.
func (cli *Client) Lookup(user string) (*Addr, error) {
return cli.getAddr(user)
}
// Send sends an encrypted message to the specified user
func (cli *Client) Send(user, msg string) error {
addr, err := cli.getAddr(user)

@ -1,6 +1,8 @@
package components
import (
"crypto/sha256"
"fmt"
"log"
"github.com/maxence-charriere/go-app/v9/pkg/app"
@ -11,12 +13,16 @@ import (
"github.com/mlctrez/goapp-mdc/pkg/textarea"
"github.com/mlctrez/goapp-mdc/pkg/textfield"
"go.mills.io/saltyim"
"go.mills.io/saltyim/internal/pwa/storage"
)
type Configuration struct {
app.Compo
base.JsUtil
// to support removal
Contacts []string
navigation *Navigation
// components
@ -34,6 +40,7 @@ func (c *Configuration) readState(ctx app.Context) {
}
c.identity.Value = string(identity.Contents())
c.user.Value = identity.Addr().String()
c.Contacts = storage.ContactsLocalStorage(ctx).List()
}
func (c *Configuration) OnMount(ctx app.Context) {
@ -79,6 +86,8 @@ func (c *Configuration) Render() app.UI {
&button.Button{Icon: string(icon.MIUpdate), Label: "update identity",
Outlined: true, Raised: true, Callback: c.updateIdentity()},
app.Hr(),
app.H4().Text("remove contacts and storage"),
c.buildDeleteContacts(),
),
),
),
@ -87,6 +96,31 @@ func (c *Configuration) Render() app.UI {
}
func (c *Configuration) buildDeleteContacts() app.UI {
return app.Range(c.Contacts).Slice(func(i int) app.UI {
return app.Div().Body(
&button.Button{
Id: fmt.Sprintf("%x", sha256.Sum256([]byte(c.Contacts[i]))),
Icon: string(icon.MIDelete),
Label: c.Contacts[i],
Raised: true, Outlined: true,
Callback: func(button app.HTMLButton) {
button.OnClick(func(ctx app.Context, e app.Event) {
storage.ConversationsLocalStorage(ctx, c.Contacts[i]).Delete()
storage.ContactsLocalStorage(ctx).Remove(c.Contacts[i])
c.Contacts = storage.ContactsLocalStorage(ctx).List()
ctx.Defer(func(context app.Context) {
ctx.NewAction("navigation.update")
c.Update()
})
})
},
},
app.Br(),
)
})
}
func (c *Configuration) newIdentity() func(button app.HTMLButton) {
return func(button app.HTMLButton) {
button.OnClick(func(ctx app.Context, e app.Event) {

@ -23,29 +23,35 @@ func (n *Navigation) Render() app.UI {
List: &list.List{Type: list.Navigation, Id: "navigationList"},
}
}
items := list.Items{
{Type: list.ItemTypeAnchor, Graphic: icon.MISettings, Href: "/config", Text: "settings"},
{Type: list.ItemTypeAnchor, Graphic: icon.MIPersonAdd, Href: "/newchat", Text: "new chat"},
{Type: list.ItemTypeAnchor, Graphic: icon.MISettings, Href: "/config", Text: "Settings"},
{Type: list.ItemTypeAnchor, Graphic: icon.MIPersonAdd, Href: "/newchat", Text: "New Chat"},
{Type: list.ItemTypeDivider},
}
for _, contact := range n.Contacts {
i := &list.Item{Type: list.ItemTypeAnchor, Graphic: icon.MIPerson, Href: "/#" + contact, Text: contact}
items = append(items, i)
}
n.drawer.List.Items = items.UIList()
return n.drawer
}
func (n *Navigation) LoadFromStorage(ctx app.Context) {
n.Contacts = storage.ContactsLocalStorage(ctx).List()
}
func (n *Navigation) OnMount(ctx app.Context) {
n.items.SelectHref(ctx.Page().URL().Path)
n.Contacts = storage.ContactsLocalStorage(ctx).List()
n.LoadFromStorage(ctx)
ctx.Handle(string(list.Select), func(context app.Context, action app.Action) {
if action.Value == n.drawer.List {
n.drawer.ActionClose(context)
}
})
ctx.Handle("navigation.update", n.navigationUpdate)
}
func (n *Navigation) navigationUpdate(ctx app.Context, action app.Action) {
n.drawer.List.Update()
}

@ -45,9 +45,9 @@ func (n *NewChat) Render() app.UI {
app.Div().Class("main-content").ID("main-content").Body(
topBar.Main().Body(
app.Div().ID("wrapper").Body(
app.H4().Text("new chat"),
app.H4().Text("New Chat"),
n.user,
&button.Button{Icon: string(icon.MICreate), Label: "new chat",
&button.Button{Icon: string(icon.MICreate), Label: "New Chat",
Outlined: true, Raised: true, Callback: n.newChat()},
n.dialog,
),

@ -32,8 +32,6 @@ type SaltyChat struct {
Friend string
chatInput *textfield.TextField
incoming chan string
}
func (h *SaltyChat) init(ctx app.Context) {
@ -82,14 +80,19 @@ func (h *SaltyChat) OnMount(ctx app.Context) {
h.refreshMessages(ctx)
if app.IsClient {
h.connect(ctx)
} else {
log.Println("app not running as a client?")
}
}
func (h *SaltyChat) connect(ctx app.Context) {
log.Println("connect()")
if client != nil {
log.Println("already have a connected client")
return
}
log.Println("creating new client")
identity, err := GetIdentityFromState(ctx)
if err != nil {
@ -115,18 +118,36 @@ func (h *SaltyChat) connect(ctx app.Context) {
switch s := s.(type) {
case *lextwt.SaltyText:
user := s.User.String()
storage.ContactsLocalStorage(ctx).Add(user)
storage.ConversationsLocalStorage(ctx, user).Append(msg.Text)
h.updateNavigation(ctx, user)
// 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
// TODO: Creates some initial content of the new chat
// to give the user a change to Accept/Reject the contact.
log.Printf("new incoming chat from %s", user)
}
}
}
})
}
func (h *SaltyChat) updateNavigation(ctx app.Context, user string) {
var existingFound bool
for _, existing := range h.navigation.Contacts {
if user == existing {
existingFound = true
}
}
if !existingFound {
ctx.NewAction("navigation.update")
}
}
func (h *SaltyChat) Render() app.UI {
topBar := &bar.TopAppBar{Title: "Salty IM",
@ -141,7 +162,6 @@ func (h *SaltyChat) Render() app.UI {
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{}
}

@ -16,6 +16,7 @@ type Conversations interface {
Read() []string
Append(line string)
Update(lines []string)
Delete()
}
type conversations struct {
@ -36,15 +37,21 @@ func (c *conversations) Append(line string) {
c.Update(append(readConversations(c.state, c.addr), line))
}
func (c *conversations) Delete() {
c.state.DelState(conversationKey(c.addr))
}
func readConversations(state StateOperations, addr string) []string {
conversationKey := fmt.Sprintf(ConversationsKey, sha256.Sum256([]byte(addr)))
var conversations []string
state.GetState(conversationKey, &conversations)
state.GetState(conversationKey(addr), &conversations)
return conversations
}
func conversationKey(addr string) string {
return fmt.Sprintf(ConversationsKey, sha256.Sum256([]byte(addr)))
}
func updateConversations(state StateOperations, addr string, conversations []string) {
conversationKey := fmt.Sprintf(ConversationsKey, sha256.Sum256([]byte(addr)))
state.SetState(conversationKey, conversations, app.Persist, app.Encrypt)
state.SetState(conversationKey(addr), conversations, app.Persist, app.Encrypt)
}
func ConversationsLocalStorage(state StateOperations, addr string) Conversations {

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7075736b07e9bd858ff854adfd5ca2814c029875d1346a0f09a8946dc11dec41
size 26695945
oid sha256:e997726e559f96e73aa2ac3302a11e7aaba80e2fb624c998bc3af0296a7e98e6
size 26674321