package ui import ( "MobileMkch/api" "MobileMkch/models" "MobileMkch/settings" "fmt" "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" "MobileMkch/cache" ) type UIManager struct { app fyne.App window fyne.Window apiClient *api.Client navigationStack []Screen currentScreen Screen mainContainer *fyne.Container headerBar *fyne.Container content *fyne.Container settings *settings.Settings } type Screen interface { GetContent() fyne.CanvasObject GetTitle() string OnShow() OnHide() } func NewUIManager(app fyne.App, window fyne.Window, apiClient *api.Client) *UIManager { appSettings, err := settings.Load() if err != nil { appSettings = &settings.Settings{ Theme: "dark", LastBoard: "", AutoRefresh: true, ShowFiles: true, CompactMode: false, Key: "", } } manager := &UIManager{ app: app, window: window, apiClient: apiClient, navigationStack: make([]Screen, 0), content: container.NewMax(), settings: appSettings, } if appSettings.Theme == "dark" { app.Settings().SetTheme(theme.DarkTheme()) } else { app.Settings().SetTheme(theme.LightTheme()) } manager.setupHeader() manager.setupMainContainer() boardsScreen := NewBoardsScreen(manager) manager.ShowScreen(boardsScreen) return manager } func (ui *UIManager) setupHeader() { backButton := widget.NewButton("<- Назад", func() { ui.GoBack() }) backButton.Hide() titleLabel := widget.NewLabel("MobileMkch") titleLabel.TextStyle = fyne.TextStyle{Bold: true} titleLabel.Alignment = fyne.TextAlignCenter refreshButton := widget.NewButton("🔄", func() { if ui.currentScreen != nil { ui.currentScreen.OnShow() } }) settingsButton := widget.NewButton("⚙️", func() { settingsScreen := NewSettingsScreen(ui) ui.ShowScreen(settingsScreen) }) rightButtons := container.NewHBox(refreshButton, settingsButton) ui.headerBar = container.NewBorder( nil, nil, backButton, rightButtons, titleLabel, ) } func (ui *UIManager) setupMainContainer() { ui.mainContainer = container.NewBorder( ui.headerBar, nil, nil, nil, ui.content, ) } func (ui *UIManager) GetMainContent() fyne.CanvasObject { return ui.mainContainer } func (ui *UIManager) ShowScreen(screen Screen) { if ui.currentScreen != nil { ui.currentScreen.OnHide() } ui.navigationStack = append(ui.navigationStack, screen) ui.currentScreen = screen ui.content.RemoveAll() ui.content.Add(screen.GetContent()) ui.updateHeader(screen.GetTitle()) ui.content.Refresh() screen.OnShow() } func (ui *UIManager) GoBack() { if len(ui.navigationStack) <= 1 { return } if ui.currentScreen != nil { ui.currentScreen.OnHide() } ui.navigationStack = ui.navigationStack[:len(ui.navigationStack)-1] ui.currentScreen = ui.navigationStack[len(ui.navigationStack)-1] ui.content.RemoveAll() ui.content.Add(ui.currentScreen.GetContent()) ui.content.Refresh() ui.updateHeader(ui.currentScreen.GetTitle()) ui.currentScreen.OnShow() } func (ui *UIManager) toggleTheme() { if ui.settings.Theme == "dark" { ui.settings.Theme = "light" ui.app.Settings().SetTheme(theme.LightTheme()) } else { ui.settings.Theme = "dark" ui.app.Settings().SetTheme(theme.DarkTheme()) } settings.Save(ui.settings) ui.window.Canvas().Refresh(ui.window.Canvas().Content()) } func (ui *UIManager) updateHeader(title string) { ui.window.SetTitle(title) if len(ui.headerBar.Objects) >= 3 { if backBtn, ok := ui.headerBar.Objects[1].(*widget.Button); ok { if len(ui.navigationStack) > 1 { backBtn.Show() } else { backBtn.Hide() } } } ui.headerBar.Refresh() } func (ui *UIManager) ShowBoardThreads(board *models.Board) { threadsScreen := NewThreadsScreen(ui, board) ui.ShowScreen(threadsScreen) } func (ui *UIManager) ShowThreadDetail(board *models.Board, thread *models.Thread) { threadScreen := NewThreadDetailScreen(ui, board, thread) ui.ShowScreen(threadScreen) } func (ui *UIManager) PushScreen(screen Screen) { ui.ShowScreen(screen) } func (ui *UIManager) GetAPIClient() *api.Client { return ui.apiClient } func (ui *UIManager) GetWindow() fyne.Window { return ui.window } func (ui *UIManager) ShowError(title, message string) { var dialog *widget.PopUp dialog = widget.NewModalPopUp( container.NewVBox( widget.NewLabel(title), widget.NewLabel(""), widget.NewLabel(message), widget.NewLabel(""), widget.NewButton("OK", func() { dialog.Hide() }), ), ui.window.Canvas(), ) dialog.Show() } func (ui *UIManager) ShowInfo(title, message string) { var dialog *widget.PopUp dialog = widget.NewModalPopUp( container.NewVBox( widget.NewLabel(title), widget.NewLabel(""), widget.NewLabel(message), widget.NewLabel(""), widget.NewButton("OK", func() { dialog.Hide() }), ), ui.window.Canvas(), ) dialog.Show() } func (ui *UIManager) ShowInfoDialog() { var dialog *widget.PopUp content := container.NewVBox( widget.NewLabel("Информация о НЕОЖИДАНЫХ проблемах"), widget.NewLabel(""), widget.NewLabel("Если тебя направили сюда, то значит\nты попал на НЕИЗВЕДАННЫЕ ТЕРРИТОРИИ"), widget.NewLabel("ДА ДА, не ослышались, это не ошибка,\nэто особенность"), widget.NewLabel("Увы, разработчик имиджборда\nвставил палки в колеса"), widget.NewLabel("И без доната ему, например,\nпостинг работать не будет"), widget.NewLabel(""), widget.NewLabel("Увы, постинг не работает без доната"), widget.NewLabel("а разработчик боится что на его сайте\nбудут спам"), widget.NewLabel("Вкратце - на сайте работает капча"), widget.NewLabel("а наличие пасскода ее для вас отключает"), widget.NewLabel("увы, конфет много,\nно на всех не хватит"), widget.NewLabel(""), widget.NewButton("Закрыть", func() { dialog.Hide() }), ) dialog = widget.NewModalPopUp(content, ui.window.Canvas()) dialog.Show() } func (ui *UIManager) GetSettings() *settings.Settings { return ui.settings } func (ui *UIManager) SaveSettings() error { return settings.Save(ui.settings) } func (ui *UIManager) RefreshCurrentScreen() { if ui.currentScreen != nil { ui.currentScreen.OnShow() } } func (ui *UIManager) ClearCacheForBoard(boardCode string) { cacheKey := "threads_" + boardCode cache.GetCache().Delete(cacheKey) } func (ui *UIManager) ClearCacheForThread(threadID int) { commentsKey := fmt.Sprintf("comments_%d", threadID) threadDetailKey := fmt.Sprintf("thread_detail_%d", threadID) cache.GetCache().Delete(commentsKey) cache.GetCache().Delete(threadDetailKey) } func (ui *UIManager) GetCurrentScreen() Screen { return ui.currentScreen } func (ui *UIManager) SetLastBoard(boardCode string) { ui.settings.LastBoard = boardCode ui.SaveSettings() } func (ui *UIManager) GetLastBoard() string { return ui.settings.LastBoard } func (ui *UIManager) GetPasscode() string { return ui.settings.Passcode } func (ui *UIManager) GetKey() string { return ui.settings.Key } func (ui *UIManager) ShowAbout() { aboutWindow := ui.app.NewWindow("Об аппке") aboutWindow.Resize(fyne.NewSize(400, 300)) content := container.NewVBox( widget.NewLabel("MobileMkch"), widget.NewLabel("Мобильный клиент для мкача"), widget.NewSeparator(), widget.NewLabel("Версия: 3.0.0-alpha (Always in alpha lol)"), widget.NewLabel("Автор: w^x (лейн, платон, а похуй как угодно)"), widget.NewLabel("Разработано с ❤️ на Go + Fyne"), ) closeButton := widget.NewButton("Закрыть", func() { aboutWindow.Close() }) content.Add(closeButton) aboutWindow.SetContent(content) aboutWindow.Show() }