@@ -33,11 +33,12 @@ import (
"mellium.im/xmpp/bookmarks"
"mellium.im/xmpp/bookmarks"
"mellium.im/xmpp/jid"
"mellium.im/xmpp/jid"
"mellium.im/xmpp/muc"
"mellium.im/xmpp/muc"
oasisSdk "pain.agency/oasis-sdk "
"mellium.im/xmpp/pubsub "
oasisSdk "github.com/sunglocto/oasis-sdk"
// TODO: integrated theme switcher
// TODO: integrated theme switcher
)
)
var version string = "3i"
var version string = "3.1 i"
var statBar widget . Label
var statBar widget . Label
var chatInfo fyne . Container
var chatInfo fyne . Container
var chatSidebar fyne . Container
var chatSidebar fyne . Container
@@ -45,6 +46,7 @@ var replyNameIcon string = ">"
var replyBodyIcon string = ">"
var replyBodyIcon string = ">"
var newlineIcon string = " |-> "
var newlineIcon string = " |-> "
var agreesToSendingHotFuckIntoChannel bool = false
var agreesToSendingHotFuckIntoChannel bool = false
var OccupantIdsToBlock = make ( map [ string ] string )
// by sunglocto
// by sunglocto
// license AGPL
// license AGPL
@@ -58,6 +60,7 @@ type Message struct {
Raw oasisSdk . XMPPChatMessage
Raw oasisSdk . XMPPChatMessage
Important bool
Important bool
Readers [ ] jid . JID
Readers [ ] jid . JID
Reactions map [ string ] string
}
}
type ChatTab struct {
type ChatTab struct {
@@ -135,31 +138,12 @@ var DMs []string
var chatTabs = make ( map [ string ] * ChatTab )
var chatTabs = make ( map [ string ] * ChatTab )
var UITabs = make ( map [ string ] * ChatTabUI )
var UITabs = make ( map [ string ] * ChatTabUI )
var AppTabs * container . AppTabs
var AppTabs * container . DocTabs
var selectedId widget . ListItemID
var selectedId widget . ListItemID
var replying bool = false
var replying bool = false
var notifications bool
var notifications bool
var connection bool = true
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 scrollDownOnNewMessage bool = true
var w fyne . Window
var w fyne . Window
var a fyne . App
var a fyne . App
@@ -189,12 +173,18 @@ func CreateUITab(chatJidStr string) ChatTabUI {
replytext . Hide ( )
replytext . Hide ( )
replytext . Importance = widget . SuccessImportance
replytext . Importance = widget . SuccessImportance
replytext . Selectable = true
replytext . Selectable = true
// replytext.Wrapping = fyne.TextWrapWord
replytext . Truncation = fyne . TextTruncateEllipsis
btn := widget . NewButtonWithIcon ( "View media" , icon , func ( ) {
btn := widget . NewButtonWithIcon ( "View media" , icon , func ( ) {
} )
} )
return container . NewVBox ( replytext , container . NewHBox ( ico , author ) , content , btn )
reactions := container . NewHBox ( )
return container . NewVBox ( replytext , container . NewHBox ( ico , author ) , content , btn , reactions )
} ,
} ,
func ( i widget . ListItemID , co fyne . CanvasObject ) {
func ( i widget . ListItemID , co fyne . CanvasObject ) {
vbox := co . ( * fyne . Container )
vbox := co . ( * fyne . Container )
authorBox := vbox . Objects [ 1 ] . ( * fyne . Container )
authorBox := vbox . Objects [ 1 ] . ( * fyne . Container )
replytext := vbox . Objects [ 0 ] . ( * widget . Label )
replytext := vbox . Objects [ 0 ] . ( * widget . Label )
@@ -211,17 +201,41 @@ func CreateUITab(chatJidStr string) ChatTabUI {
author := authorBox . Objects [ 1 ] . ( * widget . Label )
author := authorBox . Objects [ 1 ] . ( * widget . Label )
content := vbox . Objects [ 2 ] . ( * 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 )
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 {
if chatTabs [ chatJidStr ] . Messages [ i ] . Important {
content . Importance = widget . DangerImportance
content . Importance = widget . DangerImportance
}
}
btn . Hidden = true // Hide by default
btn . Hidden = true // Hide by default
msgContent := chatTabs [ chatJidStr ] . Messages [ i ] . Content
msgContent := chatTabs [ chatJidStr ] . Messages [ i ] . Content
if chatTabs [ chatJidStr ] . Messages [ i ] . ImageURL != "" {
if chatTabs [ chatJidStr ] . Messages [ i ] . Raw . OutOfBandMedia != nil {
btn . Hidden = false
btn . Hidden = false
btn . OnTapped = func ( ) {
btn . OnTapped = func ( ) {
go 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 {
if err != nil {
fyne . Do ( func ( ) {
fyne . Do ( func ( ) {
dialog . ShowError ( err , w )
dialog . ShowError ( err , w )
@@ -230,7 +244,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?
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 {
if err != nil {
fyne . Do ( func ( ) {
fyne . Do ( func ( ) {
dialog . ShowError ( err , w )
dialog . ShowError ( err , w )
@@ -263,7 +277,12 @@ func CreateUITab(chatJidStr string) ChatTabUI {
content . SetText ( msgContent )
content . SetText ( msgContent )
if chatTabs [ chatJidStr ] . Messages [ i ] . ReplyID != "PICLIENT:UNAVAILABLE" {
if chatTabs [ chatJidStr ] . Messages [ i ] . ReplyID != "PICLIENT:UNAVAILABLE" {
reply := chatTabs [ chatJidStr ] . Messages [ i ] . Raw . Reply
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
// TODO: EXPERIMENTALLY GET REPLIED TO TEXT
for i := len ( chatTabs [ chatJidStr ] . Messages ) - 1 ; i >= 0 ; i -- {
for i := len ( chatTabs [ chatJidStr ] . Messages ) - 1 ; i >= 0 ; i -- {
@@ -350,7 +369,8 @@ func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
}
}
fyne . Do ( func ( ) {
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 +442,8 @@ func dropToSignInPage(reason string) {
func main ( ) {
func main ( ) {
muc . Since ( time . Now ( ) )
muc . Since ( time . Now ( ) )
config = piConfig { }
config = piConfig { }
a = app . NewWithID ( "pi-im" )
a = app . NewWithID ( "pi-im" )
//a.Settings().SetTheme(&myTheme{})
reader , err := a . Storage ( ) . Open ( "pi.xml" )
reader , err := a . Storage ( ) . Open ( "pi.xml" )
if err != nil {
if err != nil {
dropToSignInPage ( err . Error ( ) )
dropToSignInPage ( err . Error ( ) )
@@ -460,6 +478,7 @@ func main() {
client . SetDmHandler ( func ( client * oasisSdk . XmppClient , msg * oasisSdk . XMPPChatMessage ) {
client . SetDmHandler ( func ( client * oasisSdk . XmppClient , msg * oasisSdk . XMPPChatMessage ) {
correction := false
correction := false
userJidStr := msg . From . Bare ( ) . String ( )
userJidStr := msg . From . Bare ( ) . String ( )
fmt . Println ( userJidStr )
tab , ok := chatTabs [ userJidStr ]
tab , ok := chatTabs [ userJidStr ]
if ok {
if ok {
str := * msg . CleanedBody
str := * msg . CleanedBody
@@ -495,13 +514,14 @@ func main() {
if msg . Reply == nil {
if msg . Reply == nil {
replyID = "PICLIENT:UNAVAILABLE"
replyID = "PICLIENT:UNAVAILABLE"
} else {
} else {
fmt . Println ( "Received reply in DM" )
replyID = msg . Reply . ID
replyID = msg . Reply . ID
}
}
if correction {
if correction {
for i := len ( tab . Messages ) - 1 ; i > 0 ; i -- {
for i := len ( tab . Messages ) - 1 ; i > 0 ; i -- {
if tab . Messages [ i ] . Raw . From . String ( ) == msg . From . String ( ) {
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 ( ) {
fyne . Do ( func ( ) {
UITabs [ userJidStr ] . Scroller . Refresh ( )
UITabs [ userJidStr ] . Scroller . Refresh ( )
} )
} )
@@ -511,27 +531,28 @@ func main() {
}
}
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 ,
Reactions : make ( map [ string ] string ) ,
}
}
tab . Messages = append ( tab . Messages , myMessage )
tab . Messages = append ( tab . Messages , myMessage )
fyne . Do ( func ( ) {
fyne . Do ( func ( ) {
UITabs [ userJidStr ] . Scroller . Refresh ( )
//UITabs[userJidStr].Scroller.Refresh()
if scrollDownOnNewMessage {
if scrollDownOnNewMessage {
UITabs [ userJidStr ] . Scroller . ScrollToBottom ( )
//UITabs[userJidStr].Scroller.ScrollToBottom()
}
}
} )
} )
}
}
} )
} )
client . SetGroupChatHandler ( func ( client * oasisSdk . XmppClient , muc * muc . Channel , msg * oasisSdk . XMPPChatMessage ) {
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
ignore := false
reaction := false
correction := false
correction := false
important := false
important := false
donotnotify := false
donotnotify := false
@@ -549,11 +570,17 @@ func main() {
correction = true
correction = true
break // dont need to look at more fields
break // dont need to look at more fields
}
}
if v . XMLName . Local == "reactions" {
reaction = true
break
}
}
}
var ImageID string = ""
var ImageID string = ""
mucJidStr := msg . From . Bare ( ) . String ( )
mucJidStr := msg . From . Bare ( ) . String ( )
if tab , ok := chatTabs [ mucJidStr ] ; ok {
if tab , ok := chatTabs [ mucJidStr ] ; ok {
//chatInfo.Objects[0] = widget.NewLabel(fmt.Sprintf("[!] %s", mucJidStr))
chatTabs [ mucJidStr ] . Muc = muc
chatTabs [ mucJidStr ] . Muc = muc
str := * msg . CleanedBody
str := * msg . CleanedBody
if strings . Contains ( str , login . DisplayName ) {
if strings . Contains ( str , login . DisplayName ) {
@@ -588,10 +615,22 @@ func main() {
replyID = msg . Reply . To
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 {
if correction {
for i := len ( tab . Messages ) - 1 ; i > 0 ; i -- {
for i := len ( tab . Messages ) - 1 ; i > 0 ; i -- {
if tab . Messages [ i ] . Raw . From . String ( ) == msg . From . String ( ) {
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 ( ) {
fyne . Do ( func ( ) {
UITabs [ mucJidStr ] . Scroller . Refresh ( )
UITabs [ mucJidStr ] . Scroller . Refresh ( )
} )
} )
@@ -608,6 +647,7 @@ func main() {
Raw : * msg ,
Raw : * msg ,
ImageURL : ImageID ,
ImageURL : ImageID ,
Important : important ,
Important : important ,
Reactions : make ( map [ string ] string ) ,
}
}
if ! ignore {
if ! ignore {
tab . Messages = append ( tab . Messages , myMessage )
tab . Messages = append ( tab . Messages , myMessage )
@@ -616,7 +656,10 @@ func main() {
fyne . Do ( func ( ) {
fyne . Do ( func ( ) {
UITabs [ mucJidStr ] . Scroller . Refresh ( )
UITabs [ mucJidStr ] . Scroller . Refresh ( )
if scrollDownOnNewMessage {
if scrollDownOnNewMessage {
UITabs [ mucJidStr ] . Scroller . ScrollToBottom ( )
tab , ok := UITabs [ mucJidStr ]
if ok {
tab . Scroller . ScrollToBottom ( )
}
}
}
} )
} )
}
}
@@ -649,9 +692,12 @@ func main() {
} )
} )
client . SetPresenceHandler ( func ( client * oasisSdk . XmppClient , from jid . JID , p oasisSdk . UserPresence ) {
client . SetPresenceHandler ( func ( client * oasisSdk . XmppClient , from jid . JID , p oasisSdk . UserPresence ) {
log . Println ( p . Type )
bareAcc := from . Bare ( )
bareAcc := from . Bare ( )
tab , ok := chatTabs [ bareAcc . String ( ) ]
tab , ok := chatTabs [ bareAcc . String ( ) ]
if ! ok {
if ! ok {
// User presence
addChatTab ( false , bareAcc , client . Login . DisplayName )
return
return
}
}
@@ -661,12 +707,16 @@ func main() {
} )
} )
client . SetBookmarkHandler ( false , func ( client * oasisSdk . XmppClient , bookmark bookmarks . Channel ) {
client . SetBookmarkHandler ( false , func ( client * oasisSdk . XmppClient , bookmark bookmarks . Channel ) {
if ! config . JoinBookmarks {
return
}
// FIXME
// FIXME
if bookmark . JID . String ( ) == "conversations-offtopic-reloaded@conference.trashserver.net" {
if bookmark . JID . String ( ) == "conversations-offtopic-reloaded@conference.trashserver.net" {
return
return
}
}
if bookmark . Autojoin {
if bookmark . Autojoin {
if bookmark . Nick == "" {
if bookmark . Nick == "" {
fmt . Println ( "WARNING: Bookmark has no name" )
bookmark . Nick = client . Login . DisplayName
bookmark . Nick = client . Login . DisplayName
}
}
addChatTab ( true , bookmark . JID , client . Login . DisplayName )
addChatTab ( true , bookmark . JID , client . Login . DisplayName )
@@ -723,6 +773,18 @@ func main() {
a = app . New ( )
a = app . New ( )
w = a . NewWindow ( "pi" )
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 (confirm) or minimize to the tray? (cancel)" , func ( b bool ) {
if b {
w . Close ( )
a . Quit ( )
log . Fatalln ( "Goodbye!" )
} else {
w . Hide ( )
}
} , w )
} )
w . Resize ( fyne . NewSize ( 500 , 500 ) )
w . Resize ( fyne . NewSize ( 500 , 500 ) )
entry := NewCustomMultiLineEntry ( )
entry := NewCustomMultiLineEntry ( )
@@ -783,6 +845,26 @@ func main() {
}
}
} , w )
} , 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 {
} else {
err = client . SendText ( jid . MustParse ( activeMucJid ) . Bare ( ) , text )
err = client . SendText ( jid . MustParse ( activeMucJid ) . Bare ( ) , text )
@@ -965,7 +1047,7 @@ func main() {
} , w )
} , w )
} )
} )
leaveRoom := fyne . NewMenuItem ( "Leave current room" , func ( ) {
leaveRoom := fyne . NewMenuItem ( "disconnect from current room" , func ( ) {
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
if ! ok {
if ! ok {
return
return
@@ -977,18 +1059,137 @@ func main() {
break
break
}
}
}
}
if ! chatTabs [ activeMucJid ] . isMuc {
return
}
AppTabs . Selected ( ) . Text = fmt . Sprintf ( "%s (disconnected)" , AppTabs . Selected ( ) . Text )
AppTabs . Selected ( ) . Text = fmt . Sprintf ( "%s (disconnected)" , AppTabs . Selected ( ) . Text )
AppTabs . Items = append ( AppTabs . Items [ : AppTabs . SelectedIndex ( ) ] , AppTabs . Items [ AppTabs . SelectedIndex ( ) + 1 : ] ... )
AppTabs . Items = append ( AppTabs . Items [ : AppTabs . SelectedIndex ( ) ] , AppTabs . Items [ AppTabs . SelectedIndex ( ) + 1 : ] ... )
AppTabs . SelectIndex ( 0 )
AppTabs . SelectIndex ( 0 )
err1 , err2 := client . LeaveMuc ( activeMucJid , "cya suckers! " , context . TODO ( ) )
err := client . DisconnectMuc ( activeMucJid , "user left on own accord " , context . TODO ( ) )
if err1 != nil || err2 != nil {
if err != nil {
dialog . ShowError ( errors . Join ( err1 , err2 ) , w ) // beautiful...
dialog . ShowError ( err , w )
}
}
delete ( UITabs , activeMucJid )
delete ( UITabs , activeMucJid )
} )
} )
joinARoom := fyne . NewMenuItem ( "Join a room " , func ( ) {
manageBookmarks := fyne . NewMenuItem ( "manage bookmarks " , func ( ) {
dialog . ShowEntryDialog ( "Join a room" , "JID:" , func ( s string ) {
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 )
nameLabel := widget . NewLabel ( bookmark . 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
}
var zero uint64 = uint64 ( 0 )
addChatTab ( true , bookmark . JID , bookmark . Nick )
_ , err := client . ConnectMuc ( bookmark , oasisSdk . MucLegacyHistoryConfig { MaxCount : & zero } , 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
i := resourcePiloadingGif
gif , err := extraWidgets . NewAnimatedGifFromResource ( i )
gif , err := extraWidgets . NewAnimatedGifFromResource ( i )
if err != nil {
if err != nil {
@@ -1049,109 +1250,9 @@ func main() {
} )
} )
/*
menu_help := fyne . NewMenu ( "π" , mit , reconnect , licensesbtn )
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()
/*
myBox := container.NewGridWithColumns(1, widget.NewLabel("Items"))
menu_changeroom := fyne . NewMenu ( "Α " , mic , beginADM , joinARoom , leaveRoom , manageBookmarks )
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_configureview := fyne . NewMenu ( "Β " , mia , jtt , jtb )
menu_configureview := fyne . NewMenu ( "Β " , mia , jtt , jtb )
hafjag := fyne . NewMenuItem ( "Hafjag" , func ( ) {
hafjag := fyne . NewMenuItem ( "Hafjag" , func ( ) {
entry . Text = "Hafjag"
entry . Text = "Hafjag"
@@ -1184,6 +1285,40 @@ func main() {
entry . Text = old
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 ( ) {
mycurrenttime := fyne . NewMenuItem ( "Current time" , func ( ) {
entry . Text = fmt . Sprintf ( "It is currently %s" , time . Now ( ) . Format ( time . RFC850 ) )
entry . Text = fmt . Sprintf ( "It is currently %s" , time . Now ( ) . Format ( time . RFC850 ) )
SendCallback ( )
SendCallback ( )
@@ -1240,7 +1375,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 ( ) {
bit := fyne . NewMenuItem ( "mark selected message as read" , func ( ) {
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
if ! ok {
if ! ok {
@@ -1289,6 +1424,48 @@ func main() {
dialog . ShowCustom ( "Message" , "Close" , pre , w )
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 ( ) {
red := fyne . NewMenuItem ( "show read receipts on message" , func ( ) {
pre := container . NewVBox ( )
pre := container . NewVBox ( )
@@ -1325,28 +1502,52 @@ func main() {
dialog . ShowCustom ( "Message" , "Close" , pre , w )
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 )
ma := fyne . NewMainMenu ( menu_help , menu_changeroom , menu_configureview , menu_messageoptions , menu_jokes )
w . SetMainMenu ( ma )
w . SetMainMenu ( ma )
desk , ok := a . ( desktop . App )
desk , ok := a . ( desktop . App )
if ok {
if ok {
desk . SetSystemTrayMenu ( menu_help )
desk . SetSystemTrayMenu ( fyne . NewMenu ( "" , fyne . NewMenuItem ( "show" , w . Show ) ) )
}
}
AppTabs = container . NewAppTabs (
AppTabs = container . NewDocTabs (
container . NewTabItem ( "τίποτα " , widget . NewLabel ( `
container . NewTabItem ( "... " , widget . NewLabel ( `
pi
pi
This tab will be used for displaying certain actions, such as
managing your bookmarks and configuring rooms.
` ) ) ,
` ) ) ,
)
)
/*
AppTabs . CloseIntercept = func ( ti * container . TabItem ) {
for _, mucJidStr := range login.MucsToJoin {
go func ( ) {
mucJid, err := jid.Parse(mucJidStr)
var activeChatJid string
if err == nil {
scroller , ok := ti . Content . ( * widget . List )
addChatTab(true, mucJid, login.DisplayName)
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 )
}
AppTabs . Selected ( ) . Text = fmt . Sprintf ( "%s (disconnected)" , AppTabs . Selected ( ) . Text )
AppTabs . Remove ( ti )
delete ( UITabs , activeChatJid )
} ( )
}
for _ , userJidStr := range DMs {
for _ , userJidStr := range DMs {
fmt . Println ( userJidStr )
fmt . Println ( userJidStr )
@@ -1357,6 +1558,10 @@ func main() {
}
}
AppTabs . OnSelected = func ( ti * container . TabItem ) {
AppTabs . OnSelected = func ( ti * container . TabItem ) {
if AppTabs . Selected ( ) == AppTabs . Items [ 0 ] {
chatSidebar . Hidden = true
return
}
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
if ! ok {
if ! ok {
return
return
@@ -1372,48 +1577,83 @@ func main() {
tab := chatTabs [ activeChatJid ]
tab := chatTabs [ activeChatJid ]
UITab := UITabs [ 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
chatSidebar = * UITab . Sidebar
box := container . NewVBox ( widget . NewRichTextFromMarkdown ( "# " + activeChatJid ) , widget . NewLabel ( fmt . Sprintf ( "%d members " , len ( tab . Members ) ) ) )
if tab . isMuc {
chatSidebar . Objects = [ ] fyne . CanvasObject { }
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 {
for name , p := range tab . Members {
gen , _ := identicon . New ( "github" , 5 , 3 )
log . Println ( string ( p . Type ) )
userjid , err := jid . Parse ( name )
gen , _ := identicon . New ( "github" , 5 , 3 )
if err ! = nil {
userjid , err : = jid . Parse ( name )
fmt . Println ( "ERROR: " + err . Error ( ) )
if err != nil {
continue // unrecoverable
fmt . Println ( "ERROR: " + err . Error ( ) )
}
continue // unrecoverable
nickname := userjid . Resourcepart ( )
}
ii , err := gen . Draw ( nickname )
nickname := userjid . Resourcepart ( )
mention : = func ( ) {
if nickname = = "" {
entry . SetText ( fmt . Sprintf ( "%s %s" , entry . Text , nickname ) )
continue // we got the MUC presence, do not include it in the member list
}
}
if err ! = nil {
ii , err : = gen . Draw ( nickname )
fmt . Println ( "ERROR: " + err . Error ( ) )
mention := func ( ) {
box . Add ( container . NewHBox ( widget . NewLabel ( nickname ) , widget . NewButton ( "Mention" , mention ) ) )
entry . SetText ( fmt . Sprintf ( "%s %s" , entry . Text , nickname ) )
} 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 ) ) )
}
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 . Refresh ( )
}
}
// HACK - disable chatsidebar because it's currently very buggy
chatSidebar . Hidden = false
chatSidebar . Hidden = false
statBar . SetText ( "" )
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 ( )
w . ShowAndRun ( )
}
}