MobileMkch/ui/threads_screen.go
2025-08-05 23:54:04 +03:00

269 lines
5.9 KiB
Go

package ui
import (
"fmt"
"log"
"strings"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"MobileMkch/models"
)
type ThreadsScreen struct {
uiManager *UIManager
board *models.Board
content *fyne.Container
threads []models.Thread
loading bool
debouncer *Debouncer
currentPage int
pageSize int
totalPages int
pageContainer *fyne.Container
}
func NewThreadsScreen(uiManager *UIManager, board *models.Board) *ThreadsScreen {
screen := &ThreadsScreen{
uiManager: uiManager,
board: board,
loading: false,
pageSize: uiManager.GetSettings().PageSize,
}
screen.debouncer = NewDebouncer(100*time.Millisecond, func() {
screen.displayThreads()
})
screen.setupContent()
return screen
}
func (ts *ThreadsScreen) setupContent() {
ts.content = container.NewVBox()
header := widget.NewLabel(fmt.Sprintf("/%s/ - %s", ts.board.Code, ts.board.Description))
header.TextStyle = fyne.TextStyle{Bold: true}
ts.content.Add(header)
ts.content.Add(widget.NewLabel("Загрузка тредов..."))
}
func (ts *ThreadsScreen) GetContent() fyne.CanvasObject {
return container.NewScroll(ts.content)
}
func (ts *ThreadsScreen) GetTitle() string {
return fmt.Sprintf("/%s/", ts.board.Code)
}
func (ts *ThreadsScreen) OnShow() {
if !ts.loading {
ts.loadThreads()
}
}
func (ts *ThreadsScreen) OnHide() {
// Ничего не делаем
}
func (ts *ThreadsScreen) loadThreads() {
ts.loading = true
ts.showLoading()
go func() {
defer func() {
ts.loading = false
}()
threads, err := ts.uiManager.GetAPIClient().GetThreads(ts.board.Code)
if err != nil {
log.Printf("Ошибка загрузки тредов: %v", err)
fyne.Do(func() {
ts.showError(fmt.Sprintf("Ошибка загрузки тредов:\n%v", err))
})
return
}
ts.threads = threads
fyne.Do(func() {
ts.debouncer.Trigger()
})
}()
}
func (ts *ThreadsScreen) showLoading() {
ts.content.RemoveAll()
header := widget.NewLabel(fmt.Sprintf("/%s/ - %s", ts.board.Code, ts.board.Description))
header.TextStyle = fyne.TextStyle{Bold: true}
ts.content.Add(header)
progressBar := widget.NewProgressBarInfinite()
ts.content.Add(progressBar)
progressBar.Start()
ts.content.Add(widget.NewLabel("Загрузка тредов..."))
ts.content.Refresh()
}
func (ts *ThreadsScreen) showError(message string) {
ts.content.RemoveAll()
header := widget.NewLabel(fmt.Sprintf("/%s/ - %s", ts.board.Code, ts.board.Description))
header.TextStyle = fyne.TextStyle{Bold: true}
ts.content.Add(header)
errorLabel := widget.NewLabel("❌ " + message)
errorLabel.Wrapping = fyne.TextWrapWord
ts.content.Add(errorLabel)
retryButton := widget.NewButton("Повторить", func() {
ts.loadThreads()
})
ts.content.Add(retryButton)
ts.content.Refresh()
}
func (ts *ThreadsScreen) displayThreads() {
ts.content.RemoveAll()
header := widget.NewLabel(fmt.Sprintf("/%s/ - %s", ts.board.Code, ts.board.Description))
header.TextStyle = fyne.TextStyle{Bold: true}
ts.content.Add(header)
buttonsContainer := container.NewHBox()
createThreadButton := widget.NewButton("Создать тред", func() {
createThreadScreen := NewCreateThreadScreen(ts.uiManager, ts.board.Code)
ts.uiManager.PushScreen(createThreadScreen)
})
buttonsContainer.Add(createThreadButton)
ts.content.Add(buttonsContainer)
if len(ts.threads) == 0 {
ts.content.Add(widget.NewLabel("Тредов не найдено"))
ts.content.Refresh()
return
}
ts.totalPages = (len(ts.threads) + ts.pageSize - 1) / ts.pageSize
ts.pageContainer = container.NewVBox()
start := ts.currentPage * ts.pageSize
end := start + ts.pageSize
if end > len(ts.threads) {
end = len(ts.threads)
}
for i := start; i < end; i++ {
thread := &ts.threads[i]
threadCard := ts.createThreadCard(thread)
ts.pageContainer.Add(threadCard)
}
ts.addPaginationControls()
ts.content.Add(ts.pageContainer)
ts.content.Refresh()
}
func (ts *ThreadsScreen) createThreadCard(thread *models.Thread) fyne.CanvasObject {
title := fmt.Sprintf("#%d: %s", thread.ID, thread.Title)
if len(title) > 20 {
title = title[:17] + "..."
}
info := fmt.Sprintf("Дата: %s",
thread.GetCreationTime().Format("02.01.2006 15:04"))
if thread.IsPinned() {
info += " | 📌 Закреплён"
}
if rating := thread.GetRatingValue(); rating != 0 {
info += fmt.Sprintf(" | ⭐ %d", rating)
}
if len(thread.Files) > 0 && ts.uiManager.GetSettings().ShowFiles {
info += fmt.Sprintf(" | 📎 %d", len(thread.Files))
}
preview := thread.Text
preview = strings.ReplaceAll(preview, "\n", " ")
preview = strings.ReplaceAll(preview, "\r", " ")
if len(preview) > 90 {
maxLen := 50
if len(preview) < maxLen {
maxLen = len(preview)
}
preview = preview[:maxLen] + "..."
}
buttonText := fmt.Sprintf("%s\n%s\n%s", title, info, preview)
button := widget.NewButton(buttonText, func() {
ts.uiManager.ShowThreadDetail(ts.board, thread)
})
return button
}
func (ts *ThreadsScreen) RefreshThreads() {
ts.loadThreads()
}
func (ts *ThreadsScreen) addPaginationControls() {
if ts.totalPages <= 1 {
return
}
paginationContainer := container.NewHBox()
prevButton := widget.NewButton("←", func() {
if ts.currentPage > 0 {
ts.currentPage--
ts.displayThreads()
}
})
if ts.currentPage == 0 {
prevButton.Disable()
}
paginationContainer.Add(prevButton)
pageInfo := widget.NewLabel(fmt.Sprintf("Страница %d из %d", ts.currentPage+1, ts.totalPages))
paginationContainer.Add(pageInfo)
nextButton := widget.NewButton("→", func() {
if ts.currentPage < ts.totalPages-1 {
ts.currentPage++
ts.displayThreads()
}
})
if ts.currentPage >= ts.totalPages-1 {
nextButton.Disable()
}
paginationContainer.Add(nextButton)
ts.pageContainer.Add(paginationContainer)
}
func (ts *ThreadsScreen) goToPage(page int) {
if page >= 0 && page < ts.totalPages {
ts.currentPage = page
ts.displayThreads()
}
}