package ui import ( "fmt" "log" "net/url" "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/widget" "MobileMkch/models" ) type ThreadDetailScreen struct { uiManager *UIManager board *models.Board thread *models.Thread threadDetail *models.ThreadDetail comments []models.Comment content *fyne.Container loading bool } func NewThreadDetailScreen(uiManager *UIManager, board *models.Board, thread *models.Thread) *ThreadDetailScreen { screen := &ThreadDetailScreen{ uiManager: uiManager, board: board, thread: thread, loading: false, } screen.setupContent() return screen } func (tds *ThreadDetailScreen) setupContent() { tds.content = container.NewVBox() header := widget.NewLabel(fmt.Sprintf("#%d: %s", tds.thread.ID, tds.thread.Title)) header.TextStyle = fyne.TextStyle{Bold: true} header.Wrapping = fyne.TextWrapWord tds.content.Add(header) tds.content.Add(widget.NewLabel("Загрузка деталей треда...")) } func (tds *ThreadDetailScreen) GetContent() fyne.CanvasObject { return container.NewScroll(tds.content) } func (tds *ThreadDetailScreen) GetTitle() string { return fmt.Sprintf("#%d", tds.thread.ID) } func (tds *ThreadDetailScreen) OnShow() { if !tds.loading { tds.loadThreadDetail() } } func (tds *ThreadDetailScreen) OnHide() { // Ничего не делаем } func (tds *ThreadDetailScreen) loadThreadDetail() { tds.loading = true tds.showLoading() go func() { defer func() { tds.loading = false }() threadDetail, comments, err := tds.uiManager.GetAPIClient().GetFullThread(tds.board.Code, tds.thread.ID) if err != nil { log.Printf("Ошибка загрузки треда: %v", err) fyne.Do(func() { tds.showError(fmt.Sprintf("Ошибка загрузки треда:\n%v", err)) }) return } tds.threadDetail = threadDetail tds.comments = comments fyne.Do(func() { tds.displayThreadDetail() }) }() } func (tds *ThreadDetailScreen) showLoading() { tds.content.RemoveAll() header := widget.NewLabel(fmt.Sprintf("#%d: %s", tds.thread.ID, tds.thread.Title)) header.TextStyle = fyne.TextStyle{Bold: true} header.Wrapping = fyne.TextWrapWord tds.content.Add(header) progressBar := widget.NewProgressBarInfinite() tds.content.Add(progressBar) progressBar.Start() tds.content.Add(widget.NewLabel("Загрузка треда и комментариев...")) tds.content.Refresh() } func (tds *ThreadDetailScreen) showError(message string) { tds.content.RemoveAll() header := widget.NewLabel(fmt.Sprintf("#%d: %s", tds.thread.ID, tds.thread.Title)) header.TextStyle = fyne.TextStyle{Bold: true} header.Wrapping = fyne.TextWrapWord tds.content.Add(header) errorLabel := widget.NewLabel("❌ " + message) errorLabel.Wrapping = fyne.TextWrapWord tds.content.Add(errorLabel) retryButton := widget.NewButton("Повторить", func() { tds.loadThreadDetail() }) tds.content.Add(retryButton) tds.content.Refresh() } func (tds *ThreadDetailScreen) displayThreadDetail() { tds.content.RemoveAll() if tds.threadDetail == nil { tds.showError("Не удалось загрузить тред") return } header := widget.NewLabel(fmt.Sprintf("#%d: %s", tds.threadDetail.ID, tds.threadDetail.Title)) header.TextStyle = fyne.TextStyle{Bold: true} header.Wrapping = fyne.TextWrapWord tds.content.Add(header) buttonsContainer := container.NewHBox() addCommentButton := widget.NewButton("Добавить комментарий", func() { addCommentScreen := NewAddCommentScreen(tds.uiManager, tds.board.Code, tds.thread.ID) tds.uiManager.PushScreen(addCommentScreen) }) buttonsContainer.Add(addCommentButton) refreshButton := widget.NewButton("Обновить", func() { tds.uiManager.ClearCacheForThread(tds.thread.ID) tds.loadThreadDetail() }) buttonsContainer.Add(refreshButton) tds.content.Add(buttonsContainer) threadContainer := tds.createThreadContainer(tds.threadDetail) tds.content.Add(threadContainer) tds.content.Add(widget.NewSeparator()) commentsHeader := widget.NewLabel(fmt.Sprintf("Комментарии (%d):", len(tds.comments))) commentsHeader.TextStyle = fyne.TextStyle{Bold: true} tds.content.Add(commentsHeader) if len(tds.comments) == 0 { tds.content.Add(widget.NewLabel("Комментариев пока нет")) } else { for i := range tds.comments { comment := &tds.comments[i] commentContainer := tds.createCommentContainer(comment) tds.content.Add(commentContainer) if i < len(tds.comments)-1 { tds.content.Add(widget.NewSeparator()) } } } tds.content.Refresh() } func (tds *ThreadDetailScreen) createThreadContainer(thread *models.ThreadDetail) *fyne.Container { container := container.NewVBox() info := fmt.Sprintf("Дата: %s", thread.GetCreationTime().Format("02.01.2006 15:04")) infoLabel := widget.NewLabel(info) infoLabel.TextStyle = fyne.TextStyle{Italic: true} container.Add(infoLabel) if len(thread.Files) > 0 && tds.uiManager.GetSettings().ShowFiles { filesContainer := tds.createFilesContainer(thread.Files) container.Add(filesContainer) } if thread.Text != "" { textLabel := widget.NewLabel(thread.Text) textLabel.Wrapping = fyne.TextWrapWord container.Add(textLabel) } return container } func (tds *ThreadDetailScreen) createCommentContainer(comment *models.Comment) *fyne.Container { container := container.NewVBox() info := fmt.Sprintf("ID: %d | Дата: %s", comment.ID, comment.GetCreationTime().Format("02.01.2006 15:04")) infoLabel := widget.NewLabel(info) infoLabel.TextStyle = fyne.TextStyle{Italic: true} container.Add(infoLabel) if len(comment.Files) > 0 && tds.uiManager.GetSettings().ShowFiles { filesContainer := tds.createFilesContainer(comment.Files) container.Add(filesContainer) } if comment.Text != "" { text := comment.FormatText() textLabel := widget.NewLabel(text) textLabel.Wrapping = fyne.TextWrapWord container.Add(textLabel) } return container } func (tds *ThreadDetailScreen) createFilesContainer(files []string) *fyne.Container { if len(files) == 0 { return container.NewVBox() } filesContainer := container.New(layout.NewGridLayout(2)) for _, filePath := range files { fileInfo := models.GetFileInfo(filePath) fileWidget := tds.createFileWidget(fileInfo) filesContainer.Add(fileWidget) } return container.NewVBox( widget.NewLabel(fmt.Sprintf("📎 Файлы (%d):", len(files))), filesContainer, ) } func (tds *ThreadDetailScreen) createFileWidget(fileInfo models.FileInfo) fyne.CanvasObject { if fileInfo.IsImage { button := widget.NewButton(fmt.Sprintf("🖼️ %s", fileInfo.Filename), func() { tds.openFile(fileInfo.URL) }) return button } else if fileInfo.IsVideo { button := widget.NewButton(fmt.Sprintf("🎬 %s", fileInfo.Filename), func() { tds.openFile(fileInfo.URL) }) return button } else { button := widget.NewButton(fmt.Sprintf("📄 %s", fileInfo.Filename), func() { tds.openFile(fileInfo.URL) }) return button } } func (tds *ThreadDetailScreen) openFile(fileURL string) { parsedURL, err := url.Parse(fileURL) if err != nil { tds.uiManager.ShowError("Ошибка", "Не удалось открыть файл") return } err = fyne.CurrentApp().OpenURL(parsedURL) if err != nil { tds.uiManager.ShowError("Ошибка", "Не удалось открыть файл в браузере") } } func (tds *ThreadDetailScreen) RefreshThread() { tds.loadThreadDetail() }