@@ -8,6 +8,7 @@ import (
"io"
"log"
"net/url"
"os"
"strings"
"time"
@@ -20,6 +21,7 @@ import (
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/rrivera/identicon"
// xmpp - required
"mellium.im/xmpp/disco"
@@ -33,7 +35,7 @@ import (
// TODO: integrated theme switcher
)
var version string = "3.1a"
var version string = "3.14 a"
var statBar widget . Label
var chatInfo fyne . Container
var chatSidebar fyne . Container
@@ -42,21 +44,28 @@ var chatSidebar fyne.Container
// license AGPL
type Message struct {
Author string
Content string
ID string
ReplyID string
ImageURL string
Raw oasisSdk . XMPPChatMessage
Author string
Content string
ID string
ReplyID string
ImageURL string
Raw oasisSdk . XMPPChatMessage
Important bool
}
type MucTab struct {
Jid jid . JID
Nick string
Messages [ ] Message
Scroller * widget . List
isMuc bool
Muc * muc . Channel
type ChatTab struct {
Jid jid . JID
Nick string
Messages [ ] Message
isMuc bool
Muc * muc . Channel
UpdateSidebar bool
}
type ChatTabUI struct {
Internal * ChatTab
Scroller * widget . List ` xml:"-" `
Sidebar * fyne . Container ` xml:"-" `
}
type piConfig struct {
@@ -69,8 +78,10 @@ var config piConfig
var login oasisSdk . LoginInfo
var DMs [ ] string
var chatTabs = make ( map [ string ] * MucTab )
var tabs * container . AppTabs
var chatTabs = make ( map [ string ] * ChatTab )
var UITabs = make ( map [ string ] * ChatTabUI )
var AppTabs * container . AppTabs
var selectedId widget . ListItemID
var replying bool = false
var notifications bool
@@ -101,54 +112,62 @@ var scrollDownOnNewMessage bool = true
var w fyne . Window
var a fyne . App
func addChatTab ( isMuc bool , chatJid jid . JID , nick string ) {
mucJidStr := chatJid . String ( )
if _ , ok := chatTabs [ mucJidStr ] ; ok {
// Tab already exists
return
}
tabData := & MucTab {
Jid : chatJid ,
Nick : nick ,
Messages : [ ] Message { } ,
isMuc : isMuc ,
}
func CreateUITab ( chatJidStr string ) ChatTabUI {
var scroller * widget . List
scroller = widget . NewList (
func ( ) int {
return len ( tabData . Messages )
return len ( chatTabs [ chatJidStr ] . Messages )
} ,
func ( ) fyne . CanvasObject {
gen , _ := identicon . New ( "github" , 5 , 3 )
ii , _ := gen . Draw ( "default" )
im := ii . Image ( 25 )
ico := canvas . NewImageFromImage ( im )
ico . FillMode = canvas . ImageFillOriginal
author := widget . NewLabel ( "author" )
author . TextStyle . Bold = true
content := widget . NewRichTextWithText ( "content" )
content := widget . NewLabel ( "content" )
content . Wrapping = fyne . TextWrapWord
content . Selectable = true
icon := theme . FileVideoIcon ( )
btn := widget . NewButtonWithIcon ( "View media" , icon , func ( ) {
} )
return container . NewVBox ( author , content , btn )
return container . NewVBox ( container . NewHBox ( ico , author ) , content , btn )
} ,
func ( i widget . ListItemID , co fyne . CanvasObject ) {
vbox := co . ( * fyne . Container )
author := vbox . Objects [ 0 ] . ( * widget . Label )
content := vbox . Objects [ 1 ] . ( * widget . RichText )
authorBox := vbox . Objects [ 0 ] . ( * fyne . Container )
// generate a Icon
gen , _ := identicon . New ( "github" , 5 , 3 )
ii , _ := gen . Draw ( chatTabs [ chatJidStr ] . Messages [ i ] . Author )
im := ii . Image ( 25 )
authorBox . Objects [ 0 ] = canvas . NewImageFromImage ( im )
authorBox . Objects [ 0 ] . ( * canvas . Image ) . FillMode = canvas . ImageFillOriginal
authorBox . Objects [ 0 ] . Refresh ( )
// Icon generate end
author := authorBox . Objects [ 1 ] . ( * widget . Label )
content := vbox . Objects [ 1 ] . ( * widget . Label )
btn := vbox . Objects [ 2 ] . ( * widget . Button )
if chatTabs [ chatJidStr ] . Messages [ i ] . Important {
//content.Importance = widget.DangerImportance TODO: Fix highlighting messages with mentions, it's currently broken
}
btn . Hidden = true // Hide by default
msgContent := tabData . Messages [ i ] . Content
if tabData . Messages [ i ] . ImageURL != "" {
msgContent := chatTabs [ chatJidStr ] . Messages [ i ] . Content
if chatTabs [ chatJidStr ] . Messages [ i ] . ImageURL != "" {
btn . Hidden = false
btn . OnTapped = func ( ) {
fyne . Do ( func ( ) {
u , err := storage . ParseURI ( tabData . Messages [ i ] . ImageURL )
u , err := storage . ParseURI ( chatTabs [ chatJidStr ] . Messages [ i ] . ImageURL )
if err != nil {
dialog . ShowError ( err , w )
return
}
if strings . HasSuffix ( tabData . Messages [ i ] . ImageURL , "mp4" ) {
url , err := url . Parse ( tabData . Messages [ i ] . ImageURL )
if strings . HasSuffix ( chatTabs [ chatJidStr ] . Messages [ i ] . ImageURL , "mp4" ) || strings . HasSuffix ( chatTabs [ chatJidStr ] . Messages [ i ] . ImageURL , "mp3" ) {
url , err := url . Parse ( chatTabs [ chatJidStr ] . Messages [ i ] . ImageURL )
if err != nil {
dialog . ShowError ( err , w )
return
@@ -171,27 +190,67 @@ func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
}
msgContent = strings . Join ( lines , "\n" )
content . ParseMarkdown ( msgContent )
if tabData . Messages [ i ] . ReplyID != "PICLIENT:UNAVAILABLE" {
author . SetText ( fmt . Sprintf ( "%s > %s" , tabData . Messages [ i ] . Author , jid . MustParse ( tabData . Messages [ i ] . Raw . Reply . To ) . Resourcepart ( ) ) )
//content.ParseMarkdown(msgContent)
content . SetText ( msgContent )
if chatTabs [ chatJidStr ] . Messages [ i ] . ReplyID != "PICLIENT:UNAVAILABLE" {
author . SetText ( fmt . Sprintf ( "%s > %s" , chatTabs [ chatJidStr ] . Messages [ i ] . Author , jid . MustParse ( chatTabs [ chatJidStr ] . Messages [ i ] . Raw . Reply . To ) . Resourcepart ( ) ) )
} else {
author . SetText ( tabData . Messages [ i ] . Author )
author . SetText ( chatTabs [ chatJidStr ] . Messages [ i ] . Author )
}
if strings . Split ( msgContent , " " ) [ 0 ] == "/me" {
sl := strings . Split ( msgContent , " " )
sl [ 0 ] = ""
author . SetText ( author . Text + strings . Join ( sl , " " ) )
content . SetText ( " " )
}
scroller . SetItemHeight ( i , vbox . MinSize ( ) . Height )
} ,
)
scroller . OnSelected = func ( id widget . ListItemID ) {
selectedId = id
}
myUITab := ChatTabUI { }
scroller . CreateItem ( )
myUITab . Scroller = scroller
gen , _ := identicon . New ( "github" , 50 , 20 )
ii , _ := gen . Draw ( chatJidStr )
im := ii . Image ( 250 )
imw := canvas . NewImageFromImage ( im )
imw . FillMode = canvas . ImageFillOriginal
myUITab . Sidebar = container . NewVBox ( imw )
tabData . Scroller = scroller
return myUITab
}
func addChatTab ( isMuc bool , chatJid jid . JID , nick string ) {
chatTabs [ mucJidStr ] = tabData
chatJidStr := chatJid . String ( )
if _ , ok := chatTabs [ chatJidStr ] ; ok {
// Tab already exists
return
}
tabItem := container . NewTabItem ( chatJid . Localpart ( ) , scroller )
tabs . Append ( tabItem )
myChatTab := ChatTab {
Jid : chatJid ,
Nick : nick ,
Messages : [ ] Message { } ,
isMuc : isMuc ,
}
myUITab := CreateUITab ( chatJid . String ( ) )
myUITab . Internal = & myChatTab
chatTabs [ chatJidStr ] = & myChatTab
UITabs [ chatJidStr ] = & myUITab
fyne . Do ( func ( ) {
AppTabs . Append ( container . NewTabItem ( chatJid . String ( ) , myUITab . Scroller ) )
} )
}
func dropToSignInPage ( reason string ) {
@@ -227,8 +286,7 @@ func dropToSignInPage(reason string) {
config . Login . User = userEntry . Text
config . Login . Password = passwordEntry . Text
config . Login . DisplayName = nicknameEntry . Text
config . Notifications = true
config . Login . MucsToJoin = append ( config . Login . MucsToJoin , "ringen@muc.isekai.rocks" ) // DEBUG
config . Notifications = false
bytes , err := xml . MarshalIndent ( config , "" , "\t" )
if err != nil {
@@ -263,8 +321,9 @@ func dropToSignInPage(reason string) {
func main ( ) {
muc . Since ( time . Now ( ) )
config = piConfig { }
a = app . NewWithID ( "pi-is m" )
a = app . NewWithID ( "pi-im" )
reader , err := a . Storage ( ) . Open ( "pi.xml" )
if err != nil {
dropToSignInPage ( err . Error ( ) )
@@ -305,10 +364,9 @@ func main() {
lines := strings . Split ( str , "\n" )
for i , line := range lines {
s := strings . Split ( line , " " )
for j , v := range s {
for _ , v := range s {
_ , err := url . Parse ( v )
if err == nil && strings . HasPrefix ( v , "https://" ) {
s [ j ] = fmt . Sprintf ( "[%s](%s)" , v , v )
if strings . HasSuffix ( v , ".png" ) || strings . HasSuffix ( v , ".jpg" ) || strings . HasSuffix ( v , ".jpeg" ) || strings . HasSuffix ( v , ".webp" ) || strings . HasSuffix ( v , ".mp4" ) {
img = v
}
@@ -335,9 +393,9 @@ func main() {
tab . Messages = append ( tab . Messages , myMessage )
fyne . Do ( func ( ) {
tab . Scroller . Refresh ( )
UITabs [ userJidStr ] . Scroller . Refresh ( )
if scrollDownOnNewMessage {
tab . Scroller . ScrollToBottom ( )
UITabs [ userJidStr ] . Scroller . ScrollToBottom ( )
}
} )
}
@@ -346,6 +404,7 @@ func main() {
// HACK: IGNORING ALL MESSAGES FROM CLASSIC MUC HISTORY IN PREPARATION OF MAM SUPPORT
ignore := false
correction := false
important := false
for _ , v := range msg . Unknown {
if v . XMLName . Local == "delay" { // CLasic history message
//ignore = true
@@ -364,8 +423,12 @@ func main() {
if tab , ok := chatTabs [ mucJidStr ] ; ok {
chatTabs [ mucJidStr ] . Muc = muc
str := * msg . CleanedBody
if strings . Contains ( str , login . DisplayName ) {
fmt . Println ( str )
important = true
}
if ! ignore && notifications {
if ! correction && strings . Contains ( str , login . DisplayName ) || ( msg . Reply != nil && strings . Contains ( msg . Reply . To , login . DisplayName ) ) {
if ! correction && msg . From . String ( ) != client . JID . String ( ) && strings . Contains ( str , login . DisplayName ) || ( msg . Reply != nil && strings . Contains ( msg . Reply . To , login . DisplayName ) ) {
a . SendNotification ( fyne . NewNotification ( fmt . Sprintf ( "Mentioned in %s" , mucJidStr ) , str ) )
}
}
@@ -373,11 +436,10 @@ func main() {
lines := strings . Split ( str , "\n" )
for i , line := range lines {
s := strings . Split ( line , " " )
for j , v := range s {
for _ , v := range s {
_ , err := url . Parse ( v )
if err == nil && strings . HasPrefix ( v , "https://" ) {
s [ j ] = fmt . Sprintf ( "[%s](%s)" , v , v )
if strings . HasSuffix ( v , ".png" ) || strings . HasSuffix ( v , ".jpg" ) || strings . HasSuffix ( v , ".jpeg" ) || strings . HasSuffix ( v , ".webp" ) || strings . HasSuffix ( v , ".mp4" ) {
if strings . HasSuffix ( v , ".png" ) || strings . HasSuffix ( v , ".jpg" ) || strings . HasSuffix ( v , ".jpeg" ) || strings . HasSuffix ( v , ".webp" ) || strings . HasSuffix ( v , ".mp4" ) || strings . HasSuffix ( v , ".mp3" ) {
ImageID = v
}
}
@@ -396,11 +458,11 @@ func main() {
}
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 ( ) {
tab . Messages [ i ] . Content = * msg . CleanedBody + " (edited)"
fyne . Do ( func ( ) {
tab . Scroller . Refresh ( )
UITabs [ mucJidStr ] . Scroller . Refresh ( )
} )
return
}
@@ -408,20 +470,21 @@ func main() {
}
myMessage := Message {
Author : msg . From . Resourcepart ( ) ,
Content : str ,
ID : msg . ID ,
ReplyID : replyID ,
Raw : * msg ,
ImageURL : ImageID ,
Author : msg . From . Resourcepart ( ) ,
Content : str ,
ID : msg . ID ,
ReplyID : replyID ,
Raw : * msg ,
ImageURL : ImageID ,
Important : important ,
}
if ! ignore {
tab . Messages = append ( tab . Messages , myMessage )
}
fyne . Do ( func ( ) {
tab . Scroller . Refresh ( )
UITabs [ mucJidStr ] . Scroller . Refresh ( )
if scrollDownOnNewMessage {
tab . Scroller . ScrollToBottom ( )
UITabs [ mucJidStr ] . Scroller . ScrollToBottom ( )
}
} )
}
@@ -447,7 +510,7 @@ func main() {
} )
default :
fyne . Do ( func ( ) {
statBar . SetText ( fmt . Sprint ( "Unknown state: " , state ) )
statBar . SetText ( "" )
} )
}
} ,
@@ -462,29 +525,6 @@ func main() {
log . Fatalln ( "Could not create client - " + err . Error ( ) )
}
/*
client.Session.Serve(xmpp.HandlerFunc(func(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
d := xml.NewTokenDecoder(t)
// Ignore anything that's not a message.
if start.Name.Local != "message" {
return nil
}
msg := struct {
stanza.Message
Body string `xml:"body"`
}{}
err := d.DecodeElement(&msg, start)
if err != nil {
return err
}
if msg.Body != "" {
log.Println("Got message: %q", msg.Body)
}
return nil
}))
*/
go func ( ) {
for connection {
err = client . Connect ( )
@@ -514,21 +554,21 @@ func main() {
SendCallback := func ( ) {
text := entry . Text
if tabs . Selected ( ) == nil || tabs . Selected ( ) . Content == nil {
if AppTabs . Selected ( ) == nil || AppTabs . Selected ( ) . Content == nil || text == "" {
return
}
selectedScroller , ok := tabs . Selected ( ) . Content . ( * widget . List )
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
if ! ok {
return
}
var activeMucJid string
var isMuc bool
for jid , tabData := range chatTabs {
for jid , tabData := range UITabs {
if tabData . Scroller == selectedScroller {
activeMucJid = jid
isMuc = tabData . isMuc
isMuc = chatTabs [ activeMucJid ] . isMuc
break
}
}
@@ -540,11 +580,23 @@ func main() {
go func ( ) {
if replying {
m := chatTabs [ activeMucJid ] . Messages [ selectedId ] . Raw
client . ReplyToEvent ( & m , text )
err = client . ReplyToEvent ( & m , text )
if err != nil {
dialog . ShowError ( err , w )
}
return
}
url , uerr := url . Parse ( strings . Split ( text , " " ) [ 0 ] )
if uerr == nil && strings . HasPrefix ( strings . Split ( text , " " ) [ 0 ] , "https://" ) {
err = client . SendImage ( jid . MustParse ( activeMucJid ) . Bare ( ) , text , url . String ( ) , & text )
if err != nil {
dialog . ShowError ( err , w )
}
return
}
err = client . SendText ( jid . MustParse ( activeMucJid ) . Bare ( ) , text )
if err != nil {
dialog . ShowError ( err , w )
}
@@ -558,7 +610,7 @@ func main() {
} )
fyne . Do ( func ( ) {
if scrollDownOnNewMessage {
chatTabs [ activeMucJid ] . Scroller . ScrollToBottom ( )
UITabs [ activeMucJid ] . Scroller . ScrollToBottom ( )
}
} )
}
@@ -614,7 +666,7 @@ func main() {
} )
jtb := fyne . NewMenuItem ( "jump to bottom" , func ( ) {
selectedScroller , ok := tabs . Selected ( ) . Content . ( * widget . List )
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
if ! ok {
return
}
@@ -622,7 +674,7 @@ func main() {
} )
jtt := fyne . NewMenuItem ( "jump to top" , func ( ) {
selectedScroller , ok := tabs . Selected ( ) . Content . ( * widget . List )
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
if ! ok {
return
}
@@ -663,9 +715,9 @@ func main() {
} )
deb := fyne . NewMenuItem ( "DEBUG: Attempt to get MAM history from a user" , func ( ) {
//res, err := history.Fetch(client.Ctx, history.Query{}, jid.MustParse("ringen@muc.isekai.rocks"), client.Session)
} )
//deb := fyne.NewMenuItem( "DEBUG: Attempt to get MAM history from a user", func() {
//res, err := history.Fetch(client.Ctx, history.Query{}, jid.MustParse("ringen@muc.isekai.rocks"), client.Session)
//})
mic := fyne . NewMenuItem ( "upload a file" , func ( ) {
var link string
var toperr error
@@ -691,7 +743,9 @@ func main() {
progress := make ( chan oasisSdk . UploadProgress )
myprogressbar := widget . NewProgressBar ( )
diag := dialog . NewCustom ( "Uploading file" , "Hide" , myprogressbar , w )
diag . Show ( )
fyne . Do ( func ( ) {
diag . Show ( )
} )
go func ( ) {
client . UploadFile ( client . Ctx , reader . URI ( ) . Path ( ) , progress )
} ( )
@@ -721,32 +775,72 @@ func main() {
} , w )
} )
servDisc := fyne . NewMenuItem ( "Service discovery " , func ( ) {
servDisc := fyne . NewMenuItem ( "Disco features " , func ( ) {
var search jid . JID
dialog . ShowEntryDialog ( "Disco features" , "JID: " , func ( s string ) { // TODO: replace with undeprecated widget
search , err = jid . Parse ( s )
if err != nil {
dialog . ShowError ( err , w )
return
}
myBox := container . NewVBox ( )
info , err := disco . GetInfo ( client . Ctx , "" , jid . MustParse ( "ringen@muc.isekai.rocks" ) , client . Session )
if err != nil {
dialog . ShowError ( err , w )
}
m := info . Features
for _ , v := range m {
myBox . Objects = append ( myBox . Objects , widget . NewLabel ( v . Var ) )
myBox . Refresh ( )
}
myBox := container . NewGridWithColumns ( 1 , widget . NewLabel ( "Items\na\na\na\na\na" ) )
info , err := disco . GetInfo ( client . Ctx , "" , search , client . Session )
if err != nil {
dialog . ShowError ( err , w )
return
}
m := info . Features
for _ , v := range m {
myBox . Add ( widget . NewLabel ( v . Var ) )
myBox . Refresh ( )
}
dialog . ShowCustom ( "thing s" , "cancel" , myBox , w )
dialog . ShowCustom ( "Feature s" , "cancel" , myBox , w )
} , w )
} )
menu_help := fyne . NewMenu ( "π" , mit , reconnect , deb )
menu_changeroom := fyne . NewMenu ( "β" , mic , servDisc )
menu_configureview := fyne . NewMenu ( "γ " , mia , mis , jtt , jtb )
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 )
} )
menu_help := fyne . NewMenu ( "π" , mit , reconnect , savedata )
menu_changeroom := fyne . NewMenu ( "Α " , mic , servDisc )
menu_configureview := fyne . NewMenu ( "Β " , mia , mis , jtt , jtb )
hafjag := fyne . NewMenuItem ( "Hafjag" , func ( ) {
entry . Text = "Hafjag"
SendCallback ( )
entry . Text = "Hafjag"
} )
hotfuck := fyne . NewMenuItem ( "Hot Fuck" , func ( ) {
entry . Text = "Hot Fuck"
SendCallback ( )
entry . Text = "Oh Yeah."
} )
mycurrenttime := fyne . NewMenuItem ( "Current time" , func ( ) {
entry . Text = fmt . Sprintf ( "It is currently %s" , time . Now ( ) . Format ( time . RFC850 ) )
SendCallback ( )
} )
menu_jokes := fyne . NewMenu ( "δ" , mycurrenttime , hafjag , hotfuck )
bit := fyne . NewMenuItem ( "mark selected message as read" , func ( ) {
selectedScroller , ok := tabs . Selected ( ) . Content . ( * widget . List )
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
if ! ok {
return
}
var activeMucJid string
for jid , tabData := range chatTabs {
for jid , tabData := range UITabs {
if tabData . Scroller == selectedScroller {
activeMucJid = jid
break
@@ -764,13 +858,13 @@ func main() {
bic := fyne . NewMenuItem ( "show message XML" , func ( ) {
pre := widget . NewLabel ( "" )
selectedScroller , ok := tabs . Selected ( ) . Content . ( * widget . List )
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
if ! ok {
return
}
var activeChatJid string
for jid , tabData := range chatTabs {
for jid , tabData := range UITabs {
if tabData . Scroller == selectedScroller {
activeChatJid = jid
break
@@ -788,11 +882,11 @@ func main() {
pre . Refresh ( )
dialog . ShowCustom ( "Message" , "Close" , pre , w )
} )
menu_messageoptions := fyne . NewMenu ( "Σ " , bit , bia , bic )
ma := fyne . NewMainMenu ( menu_help , menu_changeroom , menu_configureview , menu_messageoptions )
menu_messageoptions := fyne . NewMenu ( "Γ " , bit , bia , bic )
ma := fyne . NewMainMenu ( menu_help , menu_changeroom , menu_configureview , menu_messageoptions , menu_jokes )
w . SetMainMenu ( ma )
tabs = container . NewAppTabs (
AppTabs = container . NewAppTabs (
container . NewTabItem ( "τίποτα" , widget . NewLabel ( `
pi
` ) ) ,
@@ -813,14 +907,14 @@ func main() {
}
}
tabs . OnSelected = func ( ti * container . TabItem ) {
selectedScroller , ok := tabs . Selected ( ) . Content . ( * widget . List )
AppTabs . OnSelected = func ( ti * container . TabItem ) {
selectedScroller , ok := AppTabs . Selected ( ) . Content . ( * widget . List )
if ! ok {
return
}
var activeChatJid string
for jid , tabData := range chatTabs {
for jid , tabData := range UITabs {
if tabData . Scroller == selectedScroller {
activeChatJid = jid
break
@@ -828,19 +922,22 @@ 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 ( ) ) )
}
if tab . isMuc {
chatSidebar = * container . NewStack ( container . NewVScroll ( container . NewVBox ( widget . NewRichTextFromMarkdown ( fmt . Sprintf ( "# %s" , tab . Jid . String ( ) ) ) , widget . NewRichTextFromMarkdown ( tab . Muc . Addr ( ) . String ( ) ) ) ) )
//chatSidebar.Refresh()
}
chatSidebar = * UITab . Sidebar
old := chatSidebar . Position ( )
chatSidebar . Refresh ( )
chatSidebar . Move ( old )
}
statBar . SetText ( "nothing seems to be happening right now..." )
w . SetContent ( container . NewVSplit ( container . NewVSplit ( container . NewHSplit ( tabs , & chatSidebar ) , container . NewHSplit ( entry , sendbtn ) ) , container . NewHSplit ( & statBar , & chatInfo ) ) )
// HACK - disable chatsidebar because it's currently very buggy
chatSidebar . Hidden = true
statBar . SetText ( "" )
w . SetContent ( container . NewVSplit ( container . NewVSplit ( AppTabs , container . NewHSplit ( entry , sendbtn ) ) , container . NewHSplit ( & statBar , & chatInfo ) ) )
w . ShowAndRun ( )
}