add account login screen, bump version number
This commit is contained in:
227
main.go
227
main.go
@@ -5,10 +5,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
"io"
|
"io"
|
||||||
|
_ "io/fs"
|
||||||
"log"
|
"log"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/app"
|
"fyne.io/fyne/v2/app"
|
||||||
"fyne.io/fyne/v2/canvas"
|
"fyne.io/fyne/v2/canvas"
|
||||||
@@ -17,7 +19,6 @@ import (
|
|||||||
"fyne.io/fyne/v2/storage"
|
"fyne.io/fyne/v2/storage"
|
||||||
"fyne.io/fyne/v2/theme"
|
"fyne.io/fyne/v2/theme"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
_ "fyne.io/x/fyne/theme"
|
|
||||||
catppuccin "github.com/mbaklor/fyne-catppuccin"
|
catppuccin "github.com/mbaklor/fyne-catppuccin"
|
||||||
"mellium.im/xmpp/jid"
|
"mellium.im/xmpp/jid"
|
||||||
"mellium.im/xmpp/muc"
|
"mellium.im/xmpp/muc"
|
||||||
@@ -31,12 +32,12 @@ var version string = "3a"
|
|||||||
// license AGPL
|
// license AGPL
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Author string
|
Author string
|
||||||
Content string
|
Content string
|
||||||
ID string
|
ID string
|
||||||
ReplyID string
|
ReplyID string
|
||||||
ImageURL string
|
ImageURL string
|
||||||
Raw oasisSdk.XMPPChatMessage
|
Raw oasisSdk.XMPPChatMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
type MucTab struct {
|
type MucTab struct {
|
||||||
@@ -47,11 +48,21 @@ type MucTab struct {
|
|||||||
isMuc bool
|
isMuc bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type piConfig struct {
|
||||||
|
Login oasisSdk.LoginInfo
|
||||||
|
DMs []string
|
||||||
|
Notifications bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var config piConfig
|
||||||
|
var login oasisSdk.LoginInfo
|
||||||
|
var DMs []string
|
||||||
|
|
||||||
var chatTabs = make(map[string]*MucTab)
|
var chatTabs = make(map[string]*MucTab)
|
||||||
var tabs *container.AppTabs
|
var tabs *container.AppTabs
|
||||||
var selectedId widget.ListItemID
|
var selectedId widget.ListItemID
|
||||||
var replying bool = false
|
var replying bool = false
|
||||||
var notifications bool = true
|
var notifications bool
|
||||||
var connection bool = true
|
var connection bool = true
|
||||||
|
|
||||||
type myTheme struct{}
|
type myTheme struct{}
|
||||||
@@ -117,13 +128,15 @@ func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
|
|||||||
btn.Hidden = true // Hide by default
|
btn.Hidden = true // Hide by default
|
||||||
msgContent := tabData.Messages[i].Content
|
msgContent := tabData.Messages[i].Content
|
||||||
if tabData.Messages[i].ImageURL != "" {
|
if tabData.Messages[i].ImageURL != "" {
|
||||||
btn.Hidden = false
|
btn.Hidden = false
|
||||||
btn.OnTapped = func(){fyne.Do(func() {
|
btn.OnTapped = func() {
|
||||||
u, _ := storage.ParseURI(tabData.Messages[i].ImageURL)
|
fyne.Do(func() {
|
||||||
image := canvas.NewImageFromURI(u)
|
u, _ := storage.ParseURI(tabData.Messages[i].ImageURL)
|
||||||
image.FillMode = canvas.ImageFillOriginal
|
image := canvas.NewImageFromURI(u)
|
||||||
dialog.ShowCustom("Image", "Close", image, w)
|
image.FillMode = canvas.ImageFillOriginal
|
||||||
})}
|
dialog.ShowCustom("Image", "Close", image, w)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Check if the message is a quote
|
// Check if the message is a quote
|
||||||
lines := strings.Split(msgContent, "\n")
|
lines := strings.Split(msgContent, "\n")
|
||||||
@@ -155,31 +168,84 @@ func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
|
|||||||
tabs.Append(tabItem)
|
tabs.Append(tabItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func dropToSignInPage(reason string) {
|
||||||
login := oasisSdk.LoginInfo{}
|
a = app.New()
|
||||||
|
w = a.NewWindow("Welcome to Pi")
|
||||||
|
w.Resize(fyne.NewSize(500, 500))
|
||||||
|
rt := widget.NewRichTextFromMarkdown("# Welcome to pi\nIt appears you do not have a valid account configured. Let's create one!")
|
||||||
|
footer := widget.NewRichTextFromMarkdown(fmt.Sprintf("Reason for being dropped to the sign-in page:\n\n```%s```", reason))
|
||||||
|
userEntry := widget.NewEntry()
|
||||||
|
userEntry.SetPlaceHolder("Your JID")
|
||||||
|
serverEntry := widget.NewEntry()
|
||||||
|
serverEntry.SetPlaceHolder("Server and port")
|
||||||
|
passwordEntry := widget.NewPasswordEntry()
|
||||||
|
passwordEntry.SetPlaceHolder("Your Password")
|
||||||
|
nicknameEntry := widget.NewEntry()
|
||||||
|
nicknameEntry.SetPlaceHolder("Your Nickname")
|
||||||
|
|
||||||
DMs := []string{}
|
userView := widget.NewFormItem("", userEntry)
|
||||||
|
serverView := widget.NewFormItem("", serverEntry)
|
||||||
|
passwordView := widget.NewFormItem("", passwordEntry)
|
||||||
|
nicknameView := widget.NewFormItem("", nicknameEntry)
|
||||||
|
items := []*widget.FormItem{
|
||||||
|
serverView,
|
||||||
|
userView,
|
||||||
|
passwordView,
|
||||||
|
nicknameView,
|
||||||
|
}
|
||||||
|
|
||||||
|
btn := widget.NewButton("Create an account", func() {
|
||||||
|
dialog.ShowForm("Create an account", "Create", "Dismiss", items, func(b bool) {
|
||||||
|
if b {
|
||||||
|
config := piConfig{}
|
||||||
|
config.Login.Host = serverEntry.Text
|
||||||
|
config.Login.User = userEntry.Text
|
||||||
|
config.Login.Password = passwordEntry.Text
|
||||||
|
config.Login.DisplayName = nicknameEntry.Text
|
||||||
|
config.Notifications = true
|
||||||
|
|
||||||
|
bytes, err := json.MarshalIndent(config, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
dialog.ShowError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Create("pi.json")
|
||||||
|
os.WriteFile("pi.json", bytes, os.FileMode(os.O_RDWR)) // TODO: See if this works on non-unix like systems
|
||||||
|
a.SendNotification(fyne.NewNotification("Done", "Relaunch the application"))
|
||||||
|
w.Close()
|
||||||
|
}
|
||||||
|
}, w)
|
||||||
|
})
|
||||||
|
btn2 := widget.NewButton("Close pi", func() {
|
||||||
|
w.Close()
|
||||||
|
})
|
||||||
|
w.SetContent(container.NewVBox(rt, btn, btn2,footer))
|
||||||
|
w.ShowAndRun()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
config = piConfig{}
|
||||||
|
|
||||||
bytes, err := os.ReadFile("./pi.json")
|
bytes, err := os.ReadFile("./pi.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a = app.New()
|
dropToSignInPage(err.Error())
|
||||||
w = a.NewWindow("Error")
|
|
||||||
w.Resize(fyne.NewSize(500, 500))
|
|
||||||
dialog.ShowInformation("Error", fmt.Sprintf("Please make sure there is a file named pi.json in the same directory you are running this executable...\n%s", err.Error()), w)
|
|
||||||
w.ShowAndRun()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(bytes, &login)
|
|
||||||
if err != nil {
|
|
||||||
fyne.Do(func() {
|
err = json.Unmarshal(bytes, &config)
|
||||||
a = app.New()
|
if err != nil {
|
||||||
w = a.NewWindow("Error")
|
dropToSignInPage(fmt.Sprintf("Your pi.json file is invalid:\n%s", err.Error()))
|
||||||
w.Resize(fyne.NewSize(500, 500))
|
return
|
||||||
dialog.ShowError(err, w)
|
|
||||||
w.ShowAndRun()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
login = config.Login
|
||||||
|
DMs = config.DMs
|
||||||
|
notifications = config.Notifications
|
||||||
|
|
||||||
client, err := oasisSdk.CreateClient(
|
client, err := oasisSdk.CreateClient(
|
||||||
&login,
|
&login,
|
||||||
func(client *oasisSdk.XmppClient, msg *oasisSdk.XMPPChatMessage) {
|
func(client *oasisSdk.XmppClient, msg *oasisSdk.XMPPChatMessage) {
|
||||||
@@ -194,7 +260,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
var img string = ""
|
var img string = ""
|
||||||
if strings.Contains(str, "https://") {
|
if strings.Contains(str, "https://") {
|
||||||
lines := strings.Split(str, " ")
|
lines := strings.Split(str, "\n")
|
||||||
for i, line := range lines {
|
for i, line := range lines {
|
||||||
s := strings.Split(line, " ")
|
s := strings.Split(line, " ")
|
||||||
for j, v := range s {
|
for j, v := range s {
|
||||||
@@ -215,11 +281,11 @@ func main() {
|
|||||||
replyID = msg.Reply.ID
|
replyID = msg.Reply.ID
|
||||||
}
|
}
|
||||||
myMessage := Message{
|
myMessage := Message{
|
||||||
Author: msg.From.Resourcepart(),
|
Author: msg.From.Resourcepart(),
|
||||||
Content: str,
|
Content: str,
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
ReplyID: replyID,
|
ReplyID: replyID,
|
||||||
Raw: *msg,
|
Raw: *msg,
|
||||||
ImageURL: img,
|
ImageURL: img,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +309,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if strings.Contains(str, "https://") {
|
if strings.Contains(str, "https://") {
|
||||||
lines := strings.Split(str, " ")
|
lines := strings.Split(str, "\n")
|
||||||
for i, line := range lines {
|
for i, line := range lines {
|
||||||
s := strings.Split(line, " ")
|
s := strings.Split(line, " ")
|
||||||
for j, v := range s {
|
for j, v := range s {
|
||||||
@@ -396,7 +462,6 @@ func main() {
|
|||||||
entry.SetText("")
|
entry.SetText("")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
mit := fyne.NewMenuItem("about pi", func() {
|
mit := fyne.NewMenuItem("about pi", func() {
|
||||||
dialog.ShowInformation("about pi", fmt.Sprintf("the XMPP client from hell\n\npi is an experimental XMPP client\nwritten by Sunglocto in Go.\n\nVersion %s", version), w)
|
dialog.ShowInformation("about pi", fmt.Sprintf("the XMPP client from hell\n\npi is an experimental XMPP client\nwritten by Sunglocto in Go.\n\nVersion %s", version), w)
|
||||||
})
|
})
|
||||||
@@ -427,6 +492,22 @@ func main() {
|
|||||||
}
|
}
|
||||||
}, w)
|
}, w)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
jtb := fyne.NewMenuItem("jump to bottom", func() {
|
||||||
|
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
selectedScroller.ScrollToBottom()
|
||||||
|
})
|
||||||
|
|
||||||
|
jtt := fyne.NewMenuItem("jump to top", func() {
|
||||||
|
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
selectedScroller.ScrollToTop()
|
||||||
|
})
|
||||||
/*mib := fyne.NewMenuItem("Join a room", func() {
|
/*mib := fyne.NewMenuItem("Join a room", func() {
|
||||||
nickEntry := widget.NewEntry()
|
nickEntry := widget.NewEntry()
|
||||||
nickEntry.SetText(login.DisplayName)
|
nickEntry.SetText(login.DisplayName)
|
||||||
@@ -457,24 +538,56 @@ func main() {
|
|||||||
})*/
|
})*/
|
||||||
|
|
||||||
mic := fyne.NewMenuItem("upload a file", func() {
|
mic := fyne.NewMenuItem("upload a file", func() {
|
||||||
|
var link string
|
||||||
|
var bytes []byte
|
||||||
|
var toperr error
|
||||||
|
var topreader fyne.URIReadCloser
|
||||||
dialog.ShowFileOpen(func(reader fyne.URIReadCloser, err error) {
|
dialog.ShowFileOpen(func(reader fyne.URIReadCloser, err error) {
|
||||||
if err != nil {
|
|
||||||
dialog.ShowError(err, w)
|
|
||||||
}
|
|
||||||
bytes, err := io.ReadAll(reader)
|
|
||||||
link, err := client.UploadFileFromBytes(reader.URI().String(), bytes)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dialog.ShowError(err, w)
|
dialog.ShowError(err, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if reader == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bytes, toperr = io.ReadAll(reader)
|
||||||
|
topreader = reader
|
||||||
|
|
||||||
|
if toperr != nil {
|
||||||
|
dialog.ShowError(toperr, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
progress := make(chan oasisSdk.UploadProgress)
|
||||||
|
myprogressbar := widget.NewProgressBar()
|
||||||
|
dialog.ShowCustom("Uploading file", "Hide", myprogressbar, w)
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
client.UploadFileFromBytes(client.Ctx, topreader.URI().Name(), bytes, progress)
|
||||||
|
}()
|
||||||
|
for update := range progress {
|
||||||
|
myprogressbar.Value = float64(update.Percentage)
|
||||||
|
myprogressbar.Refresh()
|
||||||
|
|
||||||
|
if update.Error != nil {
|
||||||
|
dialog.ShowError(update.Error, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if update.GetURL != "" {
|
||||||
|
link = update.GetURL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
a.Clipboard().SetContent(link)
|
a.Clipboard().SetContent(link)
|
||||||
dialog.ShowInformation("file successfully uploaded\nURL copied to your clipboard", link, w)
|
dialog.ShowInformation("file successfully uploaded\nURL copied to your clipboard", link, w)
|
||||||
|
|
||||||
}, w)
|
}, w)
|
||||||
})
|
})
|
||||||
|
|
||||||
menu_help := fyne.NewMenu("π", mit)
|
menu_help := fyne.NewMenu("π", mit)
|
||||||
menu_changeroom := fyne.NewMenu("β", mic)
|
menu_changeroom := fyne.NewMenu("β", mic)
|
||||||
menu_configureview := fyne.NewMenu("γ", mia, mis)
|
menu_configureview := fyne.NewMenu("γ", mia, mis, jtt, jtb)
|
||||||
bit := fyne.NewMenuItem("mark selected message as read", func() {
|
bit := fyne.NewMenuItem("mark selected message as read", func() {
|
||||||
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
|
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -495,7 +608,29 @@ func main() {
|
|||||||
bia := fyne.NewMenuItem("toggle replying to message", func() {
|
bia := fyne.NewMenuItem("toggle replying to message", func() {
|
||||||
replying = !replying
|
replying = !replying
|
||||||
})
|
})
|
||||||
menu_messageoptions := fyne.NewMenu("Σ", bit, bia)
|
|
||||||
|
bic := fyne.NewMenuItem("open selected message in new window", func() {
|
||||||
|
pre := widget.NewMultiLineEntry()
|
||||||
|
|
||||||
|
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var activeChatJid string
|
||||||
|
for jid, tabData := range chatTabs {
|
||||||
|
if tabData.Scroller == selectedScroller {
|
||||||
|
activeChatJid = jid
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m := chatTabs[activeChatJid].Messages[selectedId].Content
|
||||||
|
pre.SetText(m)
|
||||||
|
pre.Refresh()
|
||||||
|
dialog.ShowCustom("Message", "Close", container.NewHBox(pre), w)
|
||||||
|
})
|
||||||
|
menu_messageoptions := fyne.NewMenu("Σ", bit, bia, bic)
|
||||||
ma := fyne.NewMainMenu(menu_help, menu_changeroom, menu_configureview, menu_messageoptions)
|
ma := fyne.NewMainMenu(menu_help, menu_changeroom, menu_configureview, menu_messageoptions)
|
||||||
w.SetMainMenu(ma)
|
w.SetMainMenu(ma)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user