package main import ( "fmt" "image/color" "log" "fyne.io/fyne/v2" "fyne.io/fyne/v2/app" _ "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/dialog" _ "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" _ "github.com/mbaklor/fyne-catppuccin" _ "fyne.io/x/fyne/theme" "mellium.im/xmpp/jid" "mellium.im/xmpp/muc" "mellium.im/xmpp/stanza" oasisSdk "pain.agency/oasis-sdk" ) type Message struct { Author string Content string } var Messages []Message type myTheme struct{} func (m myTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color { return theme.DefaultTheme().Color(name, variant) } func (m myTheme) Icon(name fyne.ThemeIconName) fyne.Resource { return theme.DefaultTheme().Icon(name) } func (m myTheme) Font(style fyne.TextStyle) fyne.Resource { return theme.DefaultTheme().Font(style) } func (m myTheme) Size(name fyne.ThemeSizeName) float32 { if name == theme.SizeNameHeadingText { return 18 } return theme.DefaultTheme().Size(name) } var scrollDownOnNewMessage bool = true func addMessageToView() fyne.CanvasObject { author := widget.NewLabel("") author.TextStyle.Bold = true content := widget.NewLabel("") content.Wrapping = fyne.TextWrapWord return container.NewVBox(author, content) } func main() { login := oasisSdk.LoginInfo{ Host: "sunglocto.net:5222", User: "bot2@sunglocto.net", Password: "iloverobots", DisplayName: "bot2", TLSoff: false, StartTLS: true, MucsToJoin: []string{"ringen@muc.isekai.rocks"}, } scroller := widget.NewList( func() int { return len(Messages) }, func() fyne.CanvasObject { return addMessageToView() }, func(i widget.ListItemID, co fyne.CanvasObject) { co.(*fyne.Container).Objects[0].(*widget.Label).SetText(Messages[i].Author) co.(*fyne.Container).Objects[1].(*widget.Label).SetText(Messages[i].Content) }) client, err := oasisSdk.CreateClient( &login, func(client *oasisSdk.XmppClient, msg *oasisSdk.XMPPChatMessage) { myMessage := Message{} myMessage.Author = msg.From.Resourcepart() myMessage.Content = *msg.CleanedBody Messages = append(Messages, myMessage) if scrollDownOnNewMessage { fyne.Do(func() { scroller.ScrollToBottom() }) } }, func(client *oasisSdk.XmppClient, _ *muc.Channel, msg *oasisSdk.XMPPChatMessage) { myMessage := Message{} myMessage.Author = msg.From.Resourcepart() myMessage.Content = *msg.CleanedBody Messages = append(Messages, myMessage) if scrollDownOnNewMessage { fyne.Do(func() { scroller.ScrollToBottom() }) } }, func(_ *oasisSdk.XmppClient, from jid.JID, state oasisSdk.ChatState) { //fromStr := from.String() switch state { case oasisSdk.ChatStateActive: case oasisSdk.ChatStateComposing: case oasisSdk.ChatStatePaused: case oasisSdk.ChatStateInactive: case oasisSdk.ChatStateGone: default: } }, func(_ *oasisSdk.XmppClient, from jid.JID, id string) { fmt.Printf("Delivered %s to %s", id, from.String()) }, func(_ *oasisSdk.XmppClient, from jid.JID, id string) { fmt.Printf("%s has seen %s", from.String(), id) }, ) if err != nil { log.Fatalln("Could not create client - " + err.Error()) } go func() { err = client.Connect() if err != nil { log.Fatalln("Could not connect - " + err.Error()) } }() a := app.New() a.Settings().SetTheme(myTheme{}) w := a.NewWindow("pi") w.Resize(fyne.NewSize(500, 500)) entry := widget.NewMultiLineEntry() entry.SetPlaceHolder("Say something, you know you want to.") sendbtn := widget.NewButton("Send", func() { text := entry.Text go func() { msg := oasisSdk.XMPPChatMessage{ // TODO: Remove hack when oasisSdk adds message sending (hopefully xd) Message: stanza.Message{ To: jid.MustParse("ringen@muc.isekai.rocks"), //FIXME Type: stanza.GroupChatMessage, //FIXME }, ChatMessageBody: oasisSdk.ChatMessageBody{ Body: &text, }, } err := client.Session.Encode(client.Ctx, msg) if err != nil { dialog.ShowError(err, w) } }() entry.SetText("") }) mit := fyne.NewMenuItem("About pi", func() { dialog.ShowInformation("About pi", "the XMPP client from hell\n\npi is an experimental XMPP client\nwritten by Sunglocto in Go.", w) }) mia := fyne.NewMenuItem("Configure message view", func() { ch := widget.NewCheck("", func(b bool) {}) ch.Checked = scrollDownOnNewMessage scrollView := widget.NewFormItem("Scroll to bottom on new message", ch) items := []*widget.FormItem{ scrollView, } dialog.ShowForm("Configure message view", "Apply", "Cancel", items, func(b bool) { if b { scrollDownOnNewMessage = ch.Checked } }, w) }) mib := fyne.NewMenuItem("Join a room", func() { nick := widget.NewEntry() room := widget.NewEntry() items := []*widget.FormItem{ widget.NewFormItem("Nick", nick), widget.NewFormItem("MUC address", room), } dialog.ShowForm("Join a MUC", "Join", "Cancel", items, func(b bool) { if b { fmt.Println("attempting to join MUC") fmt.Println(nick) fmt.Println(room) go func() { client.MucClient.Join(client.Ctx, jid.MustParse(room.Text), client.Session, nil) }() } }, w) }) menu_help := fyne.NewMenu("π", mit) menu_changeroom := fyne.NewMenu("β", mib) menu_configureview := fyne.NewMenu("γ", mia) ma := fyne.NewMainMenu(menu_help, menu_changeroom, menu_configureview) w.SetMainMenu(ma) tabs := container.NewAppTabs(container.NewTabItem("pi", widget.NewLabel("pi\n\nthe XMPP client from hell")), container.NewTabItem("chat", scroller)) w.SetContent(container.NewVSplit(tabs, container.NewHSplit(entry, sendbtn))) // TODO: Add send message functionality w.ShowAndRun() }