@@ -13,6 +13,9 @@ import (
"os"
"strings"
"time"
_ "embed"
_ "image/png"
_ "image/jpeg"
// gui - required
"fyne.io/fyne/v2"
@@ -33,11 +36,16 @@ import (
"mellium.im/xmpp/bookmarks"
"mellium.im/xmpp/jid"
"mellium.im/xmpp/muc"
oasisSdk "pain.agency/oasis-sdk "
"mellium.im/xmpp/stanza "
"mellium.im/xmpp/pubsub"
oasisSdk "github.com/sunglocto/oasis-sdk"
// TODO: integrated theme switcher
)
var version string = "3i"
//go:embed pi.png
var iconBytes [ ] byte
var version string = "3.1i"
var statBar widget . Label
var chatInfo fyne . Container
var chatSidebar fyne . Container
@@ -45,6 +53,7 @@ var replyNameIcon string = ">"
var replyBodyIcon string = ">"
var newlineIcon string = " |-> "
var agreesToSendingHotFuckIntoChannel bool = false
var OccupantIdsToBlock = make ( map [ string ] string )
// by sunglocto
// license AGPL
@@ -58,6 +67,7 @@ type Message struct {
Raw oasisSdk . XMPPChatMessage
Important bool
Readers [ ] jid . JID
Reactions map [ string ] string
}
type ChatTab struct {
@@ -135,31 +145,12 @@ var DMs []string
var chatTabs = make ( map [ string ] * ChatTab )
var UITabs = make ( map [ string ] * ChatTabUI )
var AppTabs * container . AppTabs
var AppTabs * container . DocTabs
var selectedId widget . ListItemID
var replying bool = false
var notifications bool
var connection bool = true
/ *
func ( m myTheme ) Font ( style fyne . TextStyle ) fyne . Resource {
return resourceAppleColorEmojiTtf
}
func ( m myTheme ) Color ( name fyne . ThemeColorName , variant fyne . ThemeVariant ) color . Color {
return adwaita . AdwaitaTheme ( ) . Color ( name , fyne . CurrentApp ( ) . Settings ( ) . ThemeVariant ( ) )
}
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 )
}
* /
var scrollDownOnNewMessage bool = true
var w fyne . Window
var a fyne . App
@@ -189,12 +180,19 @@ func CreateUITab(chatJidStr string) ChatTabUI {
replytext . Hide ( )
replytext . Importance = widget . SuccessImportance
replytext . Selectable = true
// replytext.Wrapping = fyne.TextWrapWord
replytext . Truncation = fyne . TextTruncateEllipsis
btn := widget . NewButtonWithIcon ( "View media" , icon , func ( ) {
} )
return container . NewVBox ( replytext , container . NewHBox ( ico , author ) , content , btn )
btn . Hide ( )
reactions := container . NewHBox ( )
return container . NewVBox ( replytext , container . NewHBox ( ico , author ) , content , btn , reactions )
} ,
func ( i widget . ListItemID , co fyne . CanvasObject ) {
vbox := co . ( * fyne . Container )
authorBox := vbox . Objects [ 1 ] . ( * fyne . Container )
replytext := vbox . Objects [ 0 ] . ( * widget . Label )
@@ -211,17 +209,41 @@ func CreateUITab(chatJidStr string) ChatTabUI {
author := authorBox . Objects [ 1 ] . ( * widget . Label )
content := vbox . Objects [ 2 ] . ( * widget . Label )
unknown_tags := chatTabs [ chatJidStr ] . Messages [ i ] . Raw . Unknown
for _ , v := range unknown_tags {
if v . XMLName . Local == "occupant-id" {
for _ , attr := range v . Attrs {
if attr . Name . Local == "id" {
reason , ok := OccupantIdsToBlock [ attr . Value ]
if ok {
author . SetText ( "Ignored user" )
content . SetText ( "This user is ignored due to: " + reason )
return // message is from blocked user
}
}
}
}
}
btn := vbox . Objects [ 3 ] . ( * widget . Button )
reactions := vbox . Objects [ 4 ] . ( * fyne . Container )
reactions = container . NewVBox ( )
for _ , reaction := range chatTabs [ chatJidStr ] . Messages [ i ] . Reactions {
reactions . Add ( widget . NewLabel ( reaction ) )
}
reactions . Refresh ( )
if chatTabs [ chatJidStr ] . Messages [ i ] . Important {
content . Importance = widget . DangerImportance
}
btn . Hidden = true // Hide by default
msgContent := chatTabs [ chatJidStr ] . Messages [ i ] . Content
if chatTabs [ chatJidStr ] . Messages [ i ] . ImageURL != "" {
if chatTabs [ chatJidStr ] . Messages [ i ] . Raw . OutOfBandMedia != nil {
btn . Hidden = false
btn . OnTapped = func ( ) {
go func ( ) {
u , err := storage . ParseURI ( chatTabs [ chatJidStr ] . Messages [ i ] . ImageURL )
u , err := storage . ParseURI ( chatTabs [ chatJidStr ] . Messages [ i ] . Raw . OutOfBandMedia . URL )
if err != nil {
fyne . Do ( func ( ) {
dialog . ShowError ( err , w )
@@ -230,7 +252,7 @@ func CreateUITab(chatJidStr string) ChatTabUI {
}
if strings . HasSuffix ( chatTabs [ chatJidStr ] . Messages [ i ] . ImageURL , "mp4" ) || strings . HasSuffix ( chatTabs [ chatJidStr ] . Messages [ i ] . ImageURL , "mp3" ) || strings . HasSuffix ( chatTabs [ chatJidStr ] . Messages [ i ] . ImageURL , "gif" ) || strings . HasSuffix ( chatTabs [ chatJidStr ] . Messages [ i ] . ImageURL , "webp" ) { // FIXME: This code is fucking terrible // TODO: Could check mime?
url , err := url . Parse ( chatTabs [ chatJidStr ] . Messages [ i ] . ImageURL )
url , err := url . Parse ( chatTabs [ chatJidStr ] . Messages [ i ] . Raw . OutOfBandMedia . URL )
if err != nil {
fyne . Do ( func ( ) {
dialog . ShowError ( err , w )
@@ -263,7 +285,12 @@ func CreateUITab(chatJidStr string) ChatTabUI {
content . SetText ( msgContent )
if chatTabs [ chatJidStr ] . Messages [ i ] . ReplyID != "PICLIENT:UNAVAILABLE" {
reply := chatTabs [ chatJidStr ] . Messages [ i ] . Raw . Reply
guy := jid . MustParse ( reply . To ) . Resourcepart ( )
j , err := jid . Parse ( reply . To )
if err != nil {
log . Println ( "ERR: " + err . Error ( ) ) // FIXME
return
}
guy := j . Resourcepart ( )
// TODO: EXPERIMENTALLY GET REPLIED TO TEXT
for i := len ( chatTabs [ chatJidStr ] . Messages ) - 1 ; i >= 0 ; i -- {
@@ -350,7 +377,8 @@ func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
}
fyne . Do ( func ( ) {
AppTabs . Append ( container . NewTabItemWithIcon ( chatJid . String ( ) , icon , myUITab . Scroller ) )
myTab := container . NewTabItemWithIcon ( chatJid . String ( ) , icon , myUITab . Scroller )
AppTabs . Append ( myTab )
} )
}
@@ -422,10 +450,10 @@ func dropToSignInPage(reason string) {
func main ( ) {
muc . Since ( time . Now ( ) )
config = piConfig { }
res := fyne . NewStaticResource ( "image" , iconBytes )
a = app . NewWithID ( "pi-im" )
//a.Settings().SetTheme(&myTheme{})
a . SetIcon ( res )
reader , err := a . Storage ( ) . Open ( "pi.xml" )
if err != nil {
dropToSignInPage ( err . Error ( ) )
@@ -460,6 +488,7 @@ func main() {
client . SetDmHandler ( func ( client * oasisSdk . XmppClient , msg * oasisSdk . XMPPChatMessage ) {
correction := false
userJidStr := msg . From . Bare ( ) . String ( )
fmt . Println ( userJidStr )
tab , ok := chatTabs [ userJidStr ]
if ok {
str := * msg . CleanedBody
@@ -495,13 +524,14 @@ func main() {
if msg . Reply == nil {
replyID = "PICLIENT:UNAVAILABLE"
} else {
fmt . Println ( "Received reply in DM" )
replyID = msg . Reply . ID
}
if correction {
for i := len ( tab . Messages ) - 1 ; i > 0 ; i -- {
if tab . Messages [ i ] . Raw . From . String ( ) == msg . From . String ( ) {
tab . Messages [ i ] . Content = * msg . CleanedBody + " (edi ted)"
tab . Messages [ i ] . Content = * msg . CleanedBody + " (correc ted)"
fyne . Do ( func ( ) {
UITabs [ userJidStr ] . Scroller . Refresh ( )
} )
@@ -511,27 +541,28 @@ func main() {
}
myMessage := Message {
Author : msg . From . Resourcepart ( ) ,
Content : str ,
ID : msg . ID ,
ReplyID : replyID ,
Raw : * msg ,
ImageURL : img ,
Author : msg . From . Resourcepart ( ) ,
Content : str ,
ID : msg . ID ,
ReplyID : replyID ,
Raw : * msg ,
ImageURL : img ,
Reactions : make ( map [ string ] string ) ,
}
tab . Messages = append ( tab . Messages , myMessage )
fyne . Do ( func ( ) {
UITabs [ userJidStr ] . Scroller . Refresh ( )
//UITabs[userJidStr].Scroller.Refresh()
if scrollDownOnNewMessage {
UITabs [ userJidStr ] . Scroller . ScrollToBottom ( )
//UITabs[userJidStr].Scroller.ScrollToBottom()
}
} )
}
} )
client . SetGroupChatHandler ( func ( client * oasisSdk . XmppClient , muc * muc . Channel , msg * oasisSdk . XMPPChatMessage ) {
// HACK: IGNORING ALL MESSAGES FROM CLASSIC MUC HISTORY IN PREPARATION OF MAM SUPPORT
ignore := false
reaction := false
correction := false
important := false
donotnotify := false
@@ -549,11 +580,17 @@ func main() {
correction = true
break // dont need to look at more fields
}
if v . XMLName . Local == "reactions" {
reaction = true
break
}
}
var ImageID string = ""
mucJidStr := msg . From . Bare ( ) . String ( )
if tab , ok := chatTabs [ mucJidStr ] ; ok {
//chatInfo.Objects[0] = widget.NewLabel(fmt.Sprintf("[!] %s", mucJidStr))
chatTabs [ mucJidStr ] . Muc = muc
str := * msg . CleanedBody
if strings . Contains ( str , login . DisplayName ) {
@@ -588,10 +625,22 @@ func main() {
replyID = msg . Reply . To
}
if reaction {
for i := len ( tab . Messages ) - 1 ; i > 0 ; i -- {
if tab . Messages [ i ] . Raw . StanzaID . ID == msg . Reply . ID {
tab . Messages [ i ] . Reactions [ msg . From . String ( ) ] = * msg . CleanedBody
fyne . Do ( func ( ) {
UITabs [ mucJidStr ] . Scroller . Refresh ( )
} )
return
}
}
}
if correction {
for i := len ( tab . Messages ) - 1 ; i > 0 ; i -- {
if tab . Messages [ i ] . Raw . From . String ( ) == msg . From . String ( ) {
tab . Messages [ i ] . Content = * msg . CleanedBody + " (edi ted)"
tab . Messages [ i ] . Content = * msg . CleanedBody + " (correc ted)"
fyne . Do ( func ( ) {
UITabs [ mucJidStr ] . Scroller . Refresh ( )
} )
@@ -608,6 +657,7 @@ func main() {
Raw : * msg ,
ImageURL : ImageID ,
Important : important ,
Reactions : make ( map [ string ] string ) ,
}
if ! ignore {
tab . Messages = append ( tab . Messages , myMessage )
@@ -616,7 +666,10 @@ func main() {
fyne . Do ( func ( ) {
UITabs [ mucJidStr ] . Scroller . Refresh ( )
if scrollDownOnNewMessage {
UITabs [ mucJidStr ] . Scroller . ScrollToBottom ( )
tab , ok := UITabs [ mucJidStr ]
if ok {
tab . Scroller . ScrollToBottom ( )
}
}
} )
}
@@ -649,24 +702,32 @@ func main() {
} )
client . SetPresenceHandler ( func ( client * oasisSdk . XmppClient , from jid . JID , p oasisSdk . UserPresence ) {
fmt . Println ( p . Header . Type == stanza . UnavailablePresence )
bareAcc := from . Bare ( )
tab , ok := chatTabs [ bareAcc . String ( ) ]
if ! ok {
// User presence
addChatTab ( false , bareAcc , client . Login . DisplayName )
return
}
if tab . isMuc {
fmt . Println ( p . Type )
tab . Members [ from . String ( ) ] = p
}
} )
client . SetBookmarkHandler ( false , func ( client * oasisSdk . XmppClient , bookmark bookmarks . Channel ) {
if ! config . JoinBookmarks {
return
}
// FIXME
if bookmark . JID . String ( ) == "conversations-offtopic-reloaded@conference.trashserver.net" {
return
}
if bookmark . Autojoin {
if bookmark . Nick == "" {
fmt . Println ( "WARNING: Bookmark has no name" )
bookmark . Nick = client . Login . DisplayName
}
addChatTab ( true , bookmark . JID , client . Login . DisplayName )
@@ -723,6 +784,18 @@ func main() {
a = app . New ( )
w = a . NewWindow ( "pi" )
w . SetCloseIntercept ( func ( ) {
w . RequestFocus ( )
dialog . ShowConfirm ( "Close pi" , "You hit the close button. Do you want Pi to close completely?" , func ( b bool ) {
if b {
w . Close ( )
a . Quit ( )
log . Fatalln ( "Goodbye!" )
} else {
w . Hide ( )
}
} , w )
} )
w . Resize ( fyne . NewSize ( 500 , 500 ) )
entry := NewCustomMultiLineEntry ( )
@@ -783,6 +856,26 @@ func main() {
}
} , w )
} else if text == "@here" && chatTabs [ activeMucJid ] . isMuc {
tab := chatTabs [ activeMucJid ]
dialog . ShowConfirm ( "WARNING" , fmt . Sprintf ( "There are %d members in this room.\nYou are about to mention every single one of them.\nYou may be punished if you are not a moderator of this chat.\nWould you like to continue?" , len ( tab . Members ) ) , func ( b bool ) {
if b {
text = ""
for name := range tab . Members {
text = fmt . Sprintf ( "%s %s" , text , jid . MustParse ( name ) . Resourcepart ( ) )
}
a := pubsub . Fetch ( context . TODO ( ) , client . Session , pubsub . Query { } )
log . Println ( a . Item ( ) )
err = client . SendText ( jid . MustParse ( activeMucJid ) . Bare ( ) , text )
if err != nil {
dialog . ShowError ( err , w )
}
}
} , w )
} else {
err = client . SendText ( jid . MustParse ( activeMucJid ) . Bare ( ) , text )
@@ -965,7 +1058,7 @@ func main() {
} , w )
} )
leaveRoom := fyne . NewMenuItem ( "Leave current room" , func ( ) {
leaveRoom := fyne . NewMenuItem ( "disconnect from current room" , func ( ) {
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
if ! ok {
return
@@ -977,18 +1070,142 @@ func main() {
break
}
}
if ! chatTabs [ activeMucJid ] . isMuc {
return
}
AppTabs . Selected ( ) . Text = fmt . Sprintf ( "%s (disconnected)" , AppTabs . Selected ( ) . Text )
AppTabs . Items = append ( AppTabs . Items [ : AppTabs . SelectedIndex ( ) ] , AppTabs . Items [ AppTabs . SelectedIndex ( ) + 1 : ] ... )
AppTabs . SelectIndex ( 0 )
err1 , err2 := client . LeaveMuc ( activeMucJid , "cya suckers! " , context . TODO ( ) )
if err1 != nil || err2 != nil {
dialog . ShowError ( errors . Join ( err1 , err2 ) , w ) // beautiful...
err := client . DisconnectMuc ( activeMucJid , "user left on own accord " , context . TODO ( ) )
if err != nil {
dialog . ShowError ( err , w )
}
delete ( UITabs , activeMucJid )
} )
joinARoom := fyne . NewMenuItem ( "Join a room " , func ( ) {
dialog . ShowEntryDialog ( "Join a room" , "JID:" , func ( s string ) {
manageBookmarks := fyne . NewMenuItem ( "manage bookmarks " , func ( ) {
bookmarks := client . BookmarkCache ( )
box := container . NewVBox ( )
box . Add ( widget . NewRichTextFromMarkdown ( "# Manage Bookmarks" ) )
box . Add ( widget . NewLabel ( fmt . Sprintf ( "%d bookmarks" , len ( bookmarks ) ) ) )
for address , bookmark := range bookmarks {
bookmarkWidget := container . NewGridWithColumns ( 7 )
var nameLabel * widget . RichText
if bookmark . Name != "" {
nameLabel = widget . NewRichTextFromMarkdown ( bookmark . Name )
} else {
nameLabel = widget . NewRichTextFromMarkdown ( "_no name_" )
}
nameLabel . Wrapping = fyne . TextWrapBreak
bookmarkWidget . Add ( nameLabel )
bookmarkJidWidget := widget . NewLabel ( address )
bookmarkJidWidget . TextStyle . Monospace = true
bookmarkJidWidget . Selectable = true
bookmarkJidWidget . Wrapping = fyne . TextWrapWord
bookmarkWidget . Add ( bookmarkJidWidget )
var autojoinCheck * widget . Check
var deleteBookmarkButton * widget . Button
var joinRoomButton * widget . Button
var setNick * widget . Button
joinRoomButton = widget . NewButtonWithIcon ( "Join" , theme . AccountIcon ( ) , func ( ) {
fyne . Do ( func ( ) { joinRoomButton . Disable ( ) } )
go func ( ) {
if bookmark . Nick == "" {
bookmark . Nick = client . Login . DisplayName
}
addChatTab ( true , bookmark . JID , bookmark . Nick )
_ , err := client . ConnectMuc ( bookmark , oasisSdk . MucLegacyHistoryConfig { } , context . TODO ( ) )
if err != nil {
fyne . Do ( func ( ) {
joinRoomButton . Enable ( )
dialog . ShowError ( err , w )
} )
return
}
client . RefreshBookmarks ( false )
fyne . Do ( func ( ) { joinRoomButton . Enable ( ) } )
} ( )
} )
autojoinCheck = widget . NewCheck ( "Autojoin" , func ( b bool ) {
go func ( ) {
fyne . Do ( func ( ) { autojoinCheck . Disable ( ) } )
bookmark . Autojoin = b
err := client . PublishBookmark ( bookmark , context . TODO ( ) )
if err != nil {
fyne . Do ( func ( ) {
autojoinCheck . Enable ( )
dialog . ShowError ( err , w )
} )
return
}
client . RefreshBookmarks ( false )
fyne . Do ( func ( ) { autojoinCheck . Enable ( ) } )
} ( )
} )
autojoinCheck . Checked = bookmark . Autojoin
deleteBookmarkButton = widget . NewButtonWithIcon ( "Delete" , theme . CancelIcon ( ) , func ( ) {
go func ( ) {
err := client . DeleteBookmark ( bookmark . JID , context . TODO ( ) )
if err != nil {
fyne . Do ( func ( ) {
dialog . ShowError ( err , w )
} )
return
}
client . RefreshBookmarks ( false )
bookmarkWidget . RemoveAll ( )
} ( )
} )
nickEntry := widget . NewEntry ( )
nickEntry . SetText ( bookmark . Nick )
setNick = widget . NewButton ( "Set nick" , func ( ) {
go func ( ) {
fyne . Do ( func ( ) { setNick . Disable ( ) } )
newNick := nickEntry . Text
bookmark . Nick = newNick
err := client . PublishBookmark ( bookmark , context . TODO ( ) )
if err != nil {
fyne . Do ( func ( ) {
setNick . Enable ( )
dialog . ShowError ( err , w )
} )
return
}
client . RefreshBookmarks ( false )
fyne . Do ( func ( ) { setNick . Enable ( ) } )
} ( )
} )
bookmarkWidget . Add ( autojoinCheck )
bookmarkWidget . Add ( deleteBookmarkButton )
bookmarkWidget . Add ( joinRoomButton )
bookmarkWidget . Add ( nickEntry )
bookmarkWidget . Add ( setNick )
box . Add ( bookmarkWidget )
}
myScroller := container . NewScroll ( box )
AppTabs . Items [ 0 ] . Content = myScroller
AppTabs . Items [ 0 ] . Text = "Bookmarks"
AppTabs . SelectIndex ( 0 )
chatSidebar . Hidden = true
//d := dialog.NewCustom("manage bookmarks", "cancel", myScroller, w)
//d.Show()
} )
joinARoom := fyne . NewMenuItem ( "connect to a room" , func ( ) {
dialog . ShowEntryDialog ( "connect to a room" , "JID:" , func ( s string ) {
i := resourcePiloadingGif
gif , err := extraWidgets . NewAnimatedGifFromResource ( i )
if err != nil {
@@ -1011,8 +1228,7 @@ func main() {
mychannel . Nick = login . DisplayName
//ch, err := client.MucClient.Join(client.Ctx, joinjid, client.Session)
addChatTab ( true , myjid , login . DisplayName )
num := uint64 ( 0 )
_ , err = client . ConnectMuc ( * mychannel , oasisSdk . MucLegacyHistoryConfig { MaxCount : & num } , context . TODO ( ) )
_ , err = client . ConnectMuc ( * mychannel , oasisSdk . MucLegacyHistoryConfig { } , context . TODO ( ) )
if err != nil {
d . Hide ( )
dialog . ShowError ( err , w )
@@ -1024,7 +1240,7 @@ func main() {
} , w )
} )
beginADM := fyne . NewMenuItem ( "S tart a DM" , func ( ) {
beginADM := fyne . NewMenuItem ( "s tart a DM" , func ( ) {
dialog . ShowEntryDialog ( "Start a DM" , "JID:" , func ( s string ) {
i := resourcePiloadingGif
gif , err := extraWidgets . NewAnimatedGifFromResource ( i )
@@ -1049,109 +1265,9 @@ func main() {
} )
/ *
servDisc := fyne . NewMenuItem ( "Disco features" , func ( ) {
//var search jid.JID
dialog . ShowEntryDialog ( "Disco features" , "JID: " , func ( s string ) { // TODO: replace with undeprecated widgetd
d := dialog . NewCustom ( "Please wait" , "Close" , widget . NewLabel ( "..." ) , w )
d . Show ( )
go func ( ) {
//search, err = jid.Parse(s)
//if err != nil {
// d.Hide()
// dialog.ShowError(err, w)
// return
//}
txt := ` < iq from = ' ringen @ muc . isekai . rocks / sunglocto '
to = ' ringen @ muc . iskai . rocks / snit '
type = ' get '
id = ' vc2 ' >
< vCard xmlns = ' vcard - temp ' / >
< / iq > `
var stan stanza . IQ
xml . Unmarshal ( [ ] byte ( txt ) , & stan )
if err != nil {
d . Hide ( )
dialog . ShowError ( err , w )
return
}
r , err := client . Session . EncodeIQ ( client . Ctx , stan )
if err != nil {
d . Hide ( )
dialog . ShowError ( err , w )
return
}
ra , _ := r . Token ( )
t , _ := xml . MarshalIndent ( ra , "" , "\t" )
fmt . Println ( string ( t ) )
d . Hide ( )
/ *
menu_help := fyne . NewMenu ( "π" , mit , reconnect , licensesbtn )
myBox := container . NewGridWithColumns ( 1 , widget . NewLabel ( "Items" ) )
info , err := disco . GetInfo ( client . Ctx , "" , search , client . Session )
if err != nil {
d . Hide ( )
dialog . ShowError ( err , w )
return
}
m := info . Identity
bytes , err := xml . MarshalIndent ( m , "" , "\t" )
if err != nil {
d . Hide ( )
dialog . ShowError ( err , w )
return
}
fyne . Do ( func ( ) { d . Hide ( ) } )
myBox . Objects = append ( myBox . Objects , widget . NewLabel ( string ( bytes ) ) )
dialog . ShowCustom ( "Service discovery" , "cancel" , myBox , w )
} ( )
} , w )
} )
* /
savedata := fyne . NewMenuItem ( "DEBUG: Save tab data to disk" , func ( ) {
d := [ ] ChatTab { }
for _ , v := range chatTabs {
d = append ( d , * v )
}
b , err := xml . Marshal ( d )
if err != nil {
dialog . ShowError ( err , w )
return
}
os . Create ( "test.xml" )
os . WriteFile ( "text.xml" , b , os . ModeAppend )
} )
/ * jbookmarks := fyne . NewMenuItem ( "Join rooms in bookmarks" , func ( ) {
// FIXME: Race condition
client . FetchBookmarks ( )
rooms := client . BookmarkCache ( )
for _ , v := range rooms {
go func ( ) {
if v . Autojoin == true {
joinjid , err := v . JID . WithResource ( login . DisplayName )
if err != nil {
dialog . ShowError ( err , w )
return
}
room , err := client . MucClient . Join ( client . Ctx , joinjid , client . Session )
if err != nil {
dialog . ShowError ( err , w )
return
}
client . MucChannels [ v . JID . String ( ) ] = room
addChatTab ( true , v . JID , login . DisplayName )
}
} ( )
}
} )
* /
menu_help := fyne . NewMenu ( "π" , mit , reconnect , licensesbtn , savedata )
menu_changeroom := fyne . NewMenu ( "Α " , mic , beginADM , joinARoom , leaveRoom )
menu_changeroom := fyne . NewMenu ( "Α " , mic , beginADM , joinARoom , leaveRoom , manageBookmarks )
menu_configureview := fyne . NewMenu ( "Β " , mia , jtt , jtb )
hafjag := fyne . NewMenuItem ( "Hafjag" , func ( ) {
entry . Text = "Hafjag"
@@ -1184,6 +1300,40 @@ func main() {
entry . Text = old
} )
kai := fyne . NewMenuItem ( "kai cenat beg" , func ( ) {
old := entry . Text
entry . Text = "chat will you subscribe to save the kai cenat mafiathon 3"
SendCallback ( )
entry . Text = old
} )
mipipiemi := fyne . NewMenuItem ( "mi pipi e mi" , func ( ) {
old := entry . Text
entry . Text = "mi pipi e mi"
SendCallback ( )
entry . Text = old
} )
exposed_suffix := fyne . NewMenuItem ( ".exposed" , func ( ) {
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
if ! ok {
return
}
var activeChatJid string
for jid , tabData := range UITabs {
if tabData . Scroller == selectedScroller {
activeChatJid = jid
}
}
LatestMessage := chatTabs [ activeChatJid ] . Messages [ len ( chatTabs [ activeChatJid ] . Messages ) - 1 ]
old := entry . Text
entry . Text = fmt . Sprintf ( "%s.exposed" , LatestMessage . Content )
SendCallback ( )
entry . Text = old
} )
mycurrenttime := fyne . NewMenuItem ( "Current time" , func ( ) {
entry . Text = fmt . Sprintf ( "It is currently %s" , time . Now ( ) . Format ( time . RFC850 ) )
SendCallback ( )
@@ -1240,7 +1390,7 @@ func main() {
}
} )
menu_jokes := fyne . NewMenu ( "Δ" , mycurrenttime , hafjag , hotfuck , agree , mycurrentplayingsong )
menu_jokes := fyne . NewMenu ( "Δ" , mycurrenttime , mycurrentplayingsong , hafjag , hotfuck , agree , kai , mipipiemi , exposed_suffix )
bit := fyne . NewMenuItem ( "mark selected message as read" , func ( ) {
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
if ! ok {
@@ -1289,6 +1439,48 @@ func main() {
dialog . ShowCustom ( "Message" , "Close" , pre , w )
} )
blck := fyne . NewMenuItem ( "ignore messages from this user" , func ( ) {
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
if ! ok {
return
}
var activeChatJid string
for jid , tabData := range UITabs {
if tabData . Scroller == selectedScroller {
activeChatJid = jid
break
}
}
m := chatTabs [ activeChatJid ] . Messages [ selectedId ]
unknown_tags := m . Raw . Unknown
for _ , v := range unknown_tags {
if v . XMLName . Local == "occupant-id" {
for _ , attr := range v . Attrs {
if attr . Name . Local == "id" {
occupant_id := attr . Value
reason , ok := OccupantIdsToBlock [ occupant_id ]
if ! ok {
dialog . ShowConfirm ( "ignore user" , "All messages sent by users with an occupant ID of\n" + occupant_id + "\nwill be ignored. Continue?" , func ( b bool ) {
if b {
OccupantIdsToBlock [ occupant_id ] = "User requested"
}
} , w )
} else {
dialog . ShowConfirm ( "unignore user" , "This user is currently ignored due to:\n" + reason + "\nWould you like to unignore them?" , func ( b bool ) {
if b {
delete ( OccupantIdsToBlock , occupant_id )
}
} , w )
}
}
}
}
}
} )
red := fyne . NewMenuItem ( "show read receipts on message" , func ( ) {
pre := container . NewVBox ( )
@@ -1325,28 +1517,53 @@ func main() {
dialog . ShowCustom ( "Message" , "Close" , pre , w )
} )
menu_messageoptions := fyne . NewMenu ( "Γ" , bit , bia , bic , red )
menu_messageoptions := fyne . NewMenu ( "Γ" , bit , bia , bic , red , blck )
ma := fyne . NewMainMenu ( menu_help , menu_changeroom , menu_configureview , menu_messageoptions , menu_jokes )
w . SetMainMenu ( ma )
desk , ok := a . ( desktop . App )
if ok {
desk . SetSystemTrayMenu ( menu_help )
desk . SetSystemTrayMenu ( fyne . NewMenu ( "" , fyne . NewMenuItem ( "Show window" , w . Show ) ) )
}
AppTabs = container . NewAppTabs (
container . NewTabItem ( "τίποτα " , widget . NewLabel ( `
AppTabs = container . NewDocTabs (
container . NewTabItem ( "... " , widget . NewLabel ( `
pi
This tab will be used for displaying certain actions , such as
managing your bookmarks and configuring rooms .
` ) ) ,
)
/ *
for _ , mucJidStr := range login . MucsToJoin {
mucJid , err := jid . Parse ( mucJidStr )
if err = = nil {
addChatTab ( true , mucJid , login . DisplayName )
AppTabs . CloseIntercept = func ( ti * container . TabItem ) {
go func ( ) {
var activeChatJid string
scroller , ok : = ti . Content . ( * widget . List )
if ! ok {
return
}
} * /
for jid , tabData := range UITabs {
if tabData . Scroller == scroller {
activeChatJid = jid
break
}
}
if ! chatTabs [ activeChatJid ] . isMuc {
return
}
err := client . DisconnectMuc ( activeChatJid , "user left on own accord" , context . TODO ( ) )
if err != nil {
dialog . ShowError ( err , w )
}
fyne . Do ( func ( ) {
AppTabs . Selected ( ) . Text = fmt . Sprintf ( "%s (disconnected)" , AppTabs . Selected ( ) . Text )
AppTabs . Remove ( ti )
delete ( UITabs , activeChatJid )
} )
} ( )
}
for _ , userJidStr := range DMs {
fmt . Println ( userJidStr )
@@ -1357,6 +1574,11 @@ func main() {
}
AppTabs . OnSelected = func ( ti * container . TabItem ) {
chatSidebar . Resize ( chatSidebar . Size ( ) )
if AppTabs . Selected ( ) == AppTabs . Items [ 0 ] {
chatSidebar . Hidden = true
return
}
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
if ! ok {
return
@@ -1372,48 +1594,84 @@ func main() {
tab := chatTabs [ activeChatJid ]
UITab := UITabs [ activeChatJid ]
if tab . isMuc {
//chatInfo = *container.NewHBox(widget.NewLabel(tab.Muc.Addr().String()))
} else {
chatInfo = * container . NewHBox ( widget . NewLabel ( tab . Jid . String ( ) ) )
}
chatSidebar = * UITab . Sidebar
box := container . NewVBox ( widget . NewRichTextFromMarkdown ( "# " + activeChatJid ) , widget . NewLabel ( fmt . Sprintf ( "%d members " , len ( tab . Members ) ) ) )
chatSidebar . Objects = [ ] fyne . CanvasObject { }
if tab . isMuc {
nameLabel := widget . NewRichTextFromMarkdown ( "# " + activeChatJid )
nameLabel . Truncation = fyne . TextTruncateEllipsis
memberLabel := widget . NewLabel ( fmt . Sprintf ( "%d members " , len ( tab . Members ) ) )
memberLabel . Truncation = fyne . TextTruncateEllipsis
box := container . NewVBox ( nameLabel , )
chatSidebar . Objects = [ ] fyne . CanvasObject { }
for name := range tab . Members {
gen , _ := identicon . New ( "github" , 5 , 3 )
userjid , err := jid . Parse ( name )
if err ! = nil {
fmt . Println ( "ERROR: " + err . Error ( ) )
continue // unrecoverable
}
nickname := userjid . Resourcepart ( )
ii , err := gen . Draw ( nickname )
mention : = func ( ) {
entry . SetText ( fmt . Sprintf ( "%s %s" , entry . Text , nickname ) )
}
if err ! = nil {
fmt . Println ( "ERROR: " + err . Error ( ) )
box . Add ( container . NewHBox ( widget . NewLabel ( nickname ) , widget . NewButton ( "Mention" , mention ) ) )
} else {
im := ii . Image ( 25 )
imageWidget := canvas . NewImageFromImage ( im )
imageWidget . FillMode = canvas . ImageFillOriginal
imageWidget . Refresh ( )
box . Add ( container . NewHBox ( imageWidget , widget . NewLabel ( nickname ) , widget . NewButton ( "Mention" , mention ) ) )
}
for name , p := range tab . Members {
log . Println ( string ( p . Type ) )
gen , _ := identicon . New ( "github" , 5 , 3 )
userjid , err : = jid . Parse ( name )
if err != nil {
fmt . Println ( "ERROR: " + err . Error ( ) )
continue // unrecoverable
}
nickname := userjid . Resourcepart ( )
if nickname = = "" {
continue // we got the MUC presence, do not include it in the member list
}
ii , err : = gen . Draw ( nickname )
mention := func ( ) {
entry . SetText ( fmt . Sprintf ( "%s %s" , entry . Text , nickname ) )
}
if err != nil {
fmt . Println ( "ERROR: " + err . Error ( ) )
box . Add ( container . NewHBox ( widget . NewLabel ( nickname ) , widget . NewButton ( "Mention" , mention ) ) )
} else {
im := ii . Image ( 15 )
imageWidget := canvas . NewImageFromImage ( im )
imageWidget . FillMode = canvas . ImageFillOriginal
imageWidget . Refresh ( )
nickLabel := widget . NewLabel ( nickname )
nickLabel . Selectable = true
nickLabel . Truncation = fyne . TextTruncateClip
box . Add ( container . NewHBox ( imageWidget , nickLabel ) )
if p . Status != "" {
s := widget . NewLabel ( fmt . Sprintf ( "\"%s\"" , p . Status ) )
s . Importance = widget . WarningImportance
s . TextStyle = fyne . TextStyle {
Bold : false ,
Italic : true ,
Monospace : false ,
}
s . Truncation = fyne . TextTruncateEllipsis
box . Add ( s )
}
}
}
chatSidebar = * container . NewGridWithColumns ( 1 , container . NewVScroll ( box ) )
} else {
chatSidebar = * container . NewVBox ( widget . NewRichTextFromMarkdown ( "# " + activeChatJid ) )
}
chatSidebar = * container . NewGridWithColumns ( 1 , container . NewVScroll ( box ) )
chatSidebar . Hidden = false
chatSidebar . Refresh ( )
chatSidebar . Resize ( chatSidebar . Size ( ) )
}
// HACK - disable chatsidebar because it's currently very buggy
chatSidebar . Hidden = false
statBar . SetText ( "" )
w . SetContent ( container . NewVSplit ( container . NewVSplit ( container . NewHSplit ( AppTabs , & chatSidebar ) , container . NewHSplit ( entry , container . NewGridWithRows ( 1 , sendbtn , replybtn ) ) ) , container . NewHSplit ( & statBar , & chatInfo ) ) )
chatInfo = * container . NewHBox ( widget . NewLabel ( "" ) )
MainSplit := container . NewHSplit ( AppTabs , & chatSidebar )
DownSplit := container . NewHSplit ( entry , container . NewGridWithRows ( 1 , sendbtn , replybtn ) )
BigSplit := container . NewVSplit ( MainSplit , DownSplit )
BigSplit . SetOffset ( 1 )
SmallSplit := container . NewHSplit ( & statBar , & chatInfo )
MasterSplit := container . NewVSplit ( BigSplit , SmallSplit )
MasterSplit . SetOffset ( 1 )
w . SetContent ( MasterSplit )
w . ShowAndRun ( )
}