From f3252346c454eeb736a3f7c60cfb50bb710b1286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?CORNIERE=20R=C3=A9mi?= Date: Mon, 23 Dec 2019 10:05:27 +0100 Subject: [PATCH] Added roster update to chat client example --- _examples/xmpp_chat_client/go.mod | 2 +- _examples/xmpp_chat_client/interface.go | 15 ++++-- .../xmpp_chat_client/xmpp_chat_client.go | 51 ++++++++++++++++--- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/_examples/xmpp_chat_client/go.mod b/_examples/xmpp_chat_client/go.mod index 8d510f6..267b416 100644 --- a/_examples/xmpp_chat_client/go.mod +++ b/_examples/xmpp_chat_client/go.mod @@ -6,5 +6,5 @@ require ( github.com/awesome-gocui/gocui v0.6.1-0.20191115151952-a34ffb055986 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.6.1 - gosrc.io/xmpp v0.3.1-0.20191212145100-27130d72926b + gosrc.io/xmpp v0.3.1-0.20191223080939-f8f820170e08 ) diff --git a/_examples/xmpp_chat_client/interface.go b/_examples/xmpp_chat_client/interface.go index 15a29eb..0919709 100644 --- a/_examples/xmpp_chat_client/interface.go +++ b/_examples/xmpp_chat_client/interface.go @@ -9,7 +9,7 @@ import ( ) const ( -// Windows + // Windows chatLogWindow = "clw" // Where (received and sent) messages are logged chatInputWindow = "iw" // Where messages are written rawInputWindow = "rw" // Where raw stanzas are written @@ -71,7 +71,11 @@ func layout(g *gocui.Gui) error { } v.Title = "Contacts" v.Wrap = true - v.Autoscroll = true + // If we set this to true, the contacts list will "fit" in the window but if the number + // of contacts exceeds the maximum height, some contacts will be hidden... + // If set to false, we can scroll up and down the contact list... infinitely. Meaning lower lines + // will be unlimited and empty... Didn't find a way to quickfix yet. + v.Autoscroll = false } if v, err := g.SetView(menuWindow, 0, 0, maxX/5-1, 5*maxY/6-2, 0); err != nil { @@ -215,7 +219,7 @@ func getLine(g *gocui.Gui, v *gocui.View) error { if len(cv.ViewBufferLines()) == 0 { printContactsToWindow(g, viewState.contacts) } - } else if l == disconnect { + } else if l == disconnect { maxX, maxY := g.Size() msg := "You disconnected from the server. Press enter to quit." if v, err := g.SetView(disconnectMsg, maxX/2-30, maxY/2, maxX/2-29+len(msg), maxY/2+2, 0); err != nil { @@ -226,11 +230,12 @@ func getLine(g *gocui.Gui, v *gocui.View) error { if _, err := g.SetCurrentView(disconnectMsg); err != nil { return err } - } + } killChan <- disconnectErr } else if l == askServerForRoster { chlw, _ := g.View(chatLogWindow) - fmt.Fprintln(chlw, infoFormat+" Not yet implemented !") + fmt.Fprintln(chlw, infoFormat+"Asking server for contacts list...") + rosterChan <- struct{}{} } else if l == rawMode { mw, _ := g.View(menuWindow) viewState.input = rawInputWindow diff --git a/_examples/xmpp_chat_client/xmpp_chat_client.go b/_examples/xmpp_chat_client/xmpp_chat_client.go index d28c124..1e156d3 100644 --- a/_examples/xmpp_chat_client/xmpp_chat_client.go +++ b/_examples/xmpp_chat_client/xmpp_chat_client.go @@ -5,6 +5,7 @@ xmpp_chat_client is a demo client that connect on an XMPP server to chat with ot */ import ( + "context" "encoding/xml" "errors" "flag" @@ -19,6 +20,7 @@ import ( "path" "strconv" "strings" + "time" ) const ( @@ -43,6 +45,7 @@ var ( rawTextChan = make(chan string, 5) killChan = make(chan error, 1) errChan = make(chan error) + rosterChan = make(chan struct{}) logger *log.Logger disconnectErr = errors.New("disconnecting client") @@ -182,7 +185,6 @@ func startClient(g *gocui.Gui, config *config) { // ========================== // Start working - //askForRoster(client, g) updateRosterFromConfig(g, config) // Sending the default contact in a channel. Default value is the first contact in the list from the config. viewState.currentContact = strings.Split(config.Contacts, configContactSep)[0] @@ -190,10 +192,10 @@ func startClient(g *gocui.Gui, config *config) { clw, _ := g.View(chatLogWindow) fmt.Fprintf(clw, infoFormat+"Now sending messages to "+viewState.currentContact+" in a private conversation\n") CorrespChan <- viewState.currentContact - startMessaging(client, config) + startMessaging(client, config, g) } -func startMessaging(client xmpp.Sender, config *config) { +func startMessaging(client xmpp.Sender, config *config, g *gocui.Gui) { var text string var correspondent string for { @@ -228,6 +230,8 @@ func startMessaging(client xmpp.Sender, config *config) { } case crrsp := <-CorrespChan: correspondent = crrsp + case <-rosterChan: + askForRoster(client, g, config) } } @@ -282,10 +286,43 @@ func updateRosterFromConfig(g *gocui.Gui, config *config) { viewState.contacts = append(strings.Split(config.Contacts, configContactSep), backFromContacts) } -// Updates the menu panel of the view with the current user's roster. -// Need to add support for Roster IQ stanzas to make this work. -func askForRoster(client *xmpp.Client, g *gocui.Gui) { - // Not implemented yet ! +// Updates the menu panel of the view with the current user's roster, by asking the server. +func askForRoster(client xmpp.Sender, g *gocui.Gui, config *config) { + // Craft a roster request + req := stanza.NewIQ(stanza.Attrs{From: config.Client[clientJid], Type: stanza.IQTypeGet}) + req.RosterItems() + if logger != nil { + m, _ := xml.Marshal(req) + logger.Println(string(m)) + } + ctx, _ := context.WithTimeout(context.Background(), 30*time.Second) + + // Send the roster request to the server + c, err := client.SendIQ(ctx, req) + if err != nil { + logger.Panicln(err) + } + + // Sending a IQ has a channel spawned to process the response once we receive it. + // In order not to block the client, we spawn a goroutine to update the TUI once the server has responded. + go func() { + serverResp := <-c + if logger != nil { + m, _ := xml.Marshal(serverResp) + logger.Println(string(m)) + } + // Update contacts with the response from the server + chlw, _ := g.View(chatLogWindow) + if rosterItems, ok := serverResp.Payload.(*stanza.RosterItems); ok { + viewState.contacts = []string{} + for _, item := range rosterItems.Items { + viewState.contacts = append(viewState.contacts, item.Jid) + } + fmt.Fprintln(chlw, infoFormat+"Contacts list updated !") + return + } + fmt.Fprintln(chlw, infoFormat+"Failed to update contact list !") + }() } func isDirectory(path string) (bool, error) {