v3 (fckn yeaaah ya zaebalsa)
This commit is contained in:
parent
a9d2b95886
commit
d82a294c27
52
README.md
52
README.md
@ -9,7 +9,7 @@
|
||||
- Просмотр деталей треда и комментариев
|
||||
- Поддержка изображений и видео
|
||||
- Темная/светлая тема
|
||||
- Навигация с кнопкой "Назад" (index lore)
|
||||
- Навигация с кнопкой "Назад"
|
||||
- Улучшенный заголовок с статическими кнопками
|
||||
- Система настроек с сохранением:
|
||||
- Тема (темная/светлая)
|
||||
@ -17,7 +17,14 @@
|
||||
- Автообновление
|
||||
- Показ файлов
|
||||
- Компактный режим
|
||||
- Поддержка Android и iOS (с сохранением настроек (блять нахуй я туда полез))
|
||||
- **Полная поддержка постинга:**
|
||||
- Аутентификация по ключу
|
||||
- Аутентификация по passcode
|
||||
- Создание тредов
|
||||
- Добавление комментариев
|
||||
- Автоматическое обновление после постинга
|
||||
- Кнопка "Обновить" для ручного обновления
|
||||
- Поддержка Android и iOS
|
||||
- **Оптимизации для мобильных устройств:**
|
||||
- Кэширование данных для быстрой загрузки
|
||||
- Дебаунсинг UI обновлений
|
||||
@ -34,6 +41,35 @@
|
||||
- Очистка кэша деталей тредов
|
||||
- Очистка всего кэша
|
||||
|
||||
## Аутентификация и постинг
|
||||
|
||||
### Настройка аутентификации
|
||||
|
||||
1. Откройте настройки в приложении
|
||||
2. Введите ключ аутентификации (если есть)
|
||||
3. Введите passcode для постинга (если есть)
|
||||
4. Используйте кнопки "Тест ключа" и "Тест passcode" для проверки
|
||||
|
||||
### Создание тредов
|
||||
|
||||
1. Перейдите в нужную доску
|
||||
2. Нажмите "Создать тред"
|
||||
3. Заполните заголовок и текст
|
||||
4. Нажмите "Создать тред"
|
||||
|
||||
### Добавление комментариев
|
||||
|
||||
1. Откройте тред
|
||||
2. Нажмите "Добавить комментарий"
|
||||
3. Введите текст комментария
|
||||
4. Нажмите "Добавить комментарий"
|
||||
|
||||
### Автоматическое обновление
|
||||
|
||||
- После создания треда автоматически обновляется список тредов
|
||||
- После добавления комментария автоматически обновляется список комментариев
|
||||
- Используйте кнопку "Обновить" для принудительного обновления
|
||||
|
||||
## Сборка
|
||||
|
||||
### Desktop
|
||||
@ -97,7 +133,6 @@ open MobileMkch.xcodeproj
|
||||
|
||||
**✅ iOS И Android сборка протестирована и работает!**
|
||||
|
||||
|
||||
## Требования
|
||||
|
||||
- Go 1.24+
|
||||
@ -109,12 +144,13 @@ open MobileMkch.xcodeproj
|
||||
|
||||
- Go 1.24+
|
||||
- Fyne v2.6.2
|
||||
- HTTP клиент для API
|
||||
- HTTP клиент для API с поддержкой сессий
|
||||
- Система кэширования с TTL
|
||||
|
||||
## Структура
|
||||
|
||||
- `main.go` - точка входа
|
||||
- `api/client.go` - HTTP клиент для mkch API
|
||||
- `api/client.go` - HTTP клиент для mkch API с поддержкой аутентификации
|
||||
- `models/models.go` - структуры данных
|
||||
- `settings/settings.go` - система настроек
|
||||
- `cache/cache.go` - система кэширования с поддержкой пагинации
|
||||
@ -122,7 +158,9 @@ open MobileMkch.xcodeproj
|
||||
- `manager.go` - управление экранами
|
||||
- `boards_screen.go` - список досок
|
||||
- `threads_screen.go` - треды доски
|
||||
- `thread_detail_screen.go` - детали треда
|
||||
- `settings_screen.go` - экран настроек
|
||||
- `thread_detail_screen.go` - детали треда с кнопкой обновления
|
||||
- `create_thread_screen.go` - создание тредов
|
||||
- `add_comment_screen.go` - добавление комментариев
|
||||
- `settings_screen.go` - экран настроек с тестированием аутентификации
|
||||
- `optimization.go` - утилиты оптимизации
|
||||
- `mobile_optimizations.go` - оптимизации для мобильных устройств
|
||||
252
api/client.go
252
api/client.go
@ -1,10 +1,14 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"MobileMkch/cache"
|
||||
@ -20,20 +24,121 @@ type Client struct {
|
||||
httpClient *http.Client
|
||||
baseURL string
|
||||
debug bool
|
||||
authKey string
|
||||
passcode string
|
||||
}
|
||||
|
||||
func NewClient() *Client {
|
||||
jar, _ := cookiejar.New(nil)
|
||||
return &Client{
|
||||
httpClient: &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
Jar: jar,
|
||||
},
|
||||
baseURL: ApiURL,
|
||||
baseURL: BaseURL,
|
||||
debug: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) EnableDebug(enable bool) {
|
||||
c.debug = enable
|
||||
if c.debug {
|
||||
fmt.Printf("[DEBUG] Debug режим включен\n")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Authenticate(authKey string) error {
|
||||
resp, err := c.httpClient.Get(fmt.Sprintf("%s/key/auth/", c.baseURL))
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка получения формы аутентификации: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("ошибка получения формы аутентификации: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка чтения формы аутентификации: %w", err)
|
||||
}
|
||||
|
||||
csrfToken := extractCSRFToken(string(body))
|
||||
if csrfToken == "" {
|
||||
return fmt.Errorf("не удалось извлечь CSRF токен для аутентификации")
|
||||
}
|
||||
|
||||
formData := url.Values{}
|
||||
formData.Set("csrfmiddlewaretoken", csrfToken)
|
||||
formData.Set("key", authKey)
|
||||
|
||||
req, err := http.NewRequest("POST", fmt.Sprintf("%s/key/auth/", c.baseURL), bytes.NewBufferString(formData.Encode()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка создания запроса аутентификации: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("Referer", fmt.Sprintf("%s/key/auth/", c.baseURL))
|
||||
|
||||
resp, err = c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка отправки аутентификации: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusFound {
|
||||
return fmt.Errorf("ошибка аутентификации: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
c.authKey = authKey
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) LoginWithPasscode(passcode string) error {
|
||||
resp, err := c.httpClient.Get(fmt.Sprintf("%s/passcode/enter/", c.baseURL))
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка получения формы passcode: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("ошибка получения формы passcode: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка чтения формы passcode: %w", err)
|
||||
}
|
||||
|
||||
csrfToken := extractCSRFToken(string(body))
|
||||
if csrfToken == "" {
|
||||
return fmt.Errorf("не удалось извлечь CSRF токен для passcode")
|
||||
}
|
||||
|
||||
formData := url.Values{}
|
||||
formData.Set("csrfmiddlewaretoken", csrfToken)
|
||||
formData.Set("passcode", passcode)
|
||||
|
||||
req, err := http.NewRequest("POST", fmt.Sprintf("%s/passcode/enter/", c.baseURL), bytes.NewBufferString(formData.Encode()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка создания запроса passcode: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("Referer", fmt.Sprintf("%s/passcode/enter/", c.baseURL))
|
||||
|
||||
resp, err = c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка отправки passcode: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusFound {
|
||||
return fmt.Errorf("ошибка входа с passcode: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
c.passcode = passcode
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) makeRequest(url string) ([]byte, error) {
|
||||
@ -78,7 +183,7 @@ func (c *Client) GetBoards() ([]models.Board, error) {
|
||||
return boards, nil
|
||||
}
|
||||
|
||||
url := c.baseURL + "/boards/"
|
||||
url := ApiURL + "/boards/"
|
||||
body, err := c.makeRequest(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ошибка получения досок: %w", err)
|
||||
@ -111,7 +216,7 @@ func (c *Client) GetThreads(boardCode string) ([]models.Thread, error) {
|
||||
return threads, nil
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/board/%s", c.baseURL, boardCode)
|
||||
url := fmt.Sprintf("%s/board/%s", ApiURL, boardCode)
|
||||
body, err := c.makeRequest(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ошибка получения тредов доски %s: %w", boardCode, err)
|
||||
@ -126,13 +231,6 @@ func (c *Client) GetThreads(boardCode string) ([]models.Thread, error) {
|
||||
return nil, fmt.Errorf("ошибка парсинга тредов: %w", err)
|
||||
}
|
||||
|
||||
// if len(threads) > 12 {
|
||||
// if c.debug {
|
||||
// fmt.Printf("[API] Ограничиваем количество тредов с %d до 5\n", len(threads))
|
||||
// }
|
||||
// threads = threads[:12]
|
||||
// }
|
||||
|
||||
cache.GetCache().SetThreads(boardCode, threads)
|
||||
|
||||
if c.debug {
|
||||
@ -150,7 +248,7 @@ func (c *Client) GetThread(boardCode string, threadID int) (*models.ThreadDetail
|
||||
return thread, nil
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/board/%s/thread/%d", c.baseURL, boardCode, threadID)
|
||||
url := fmt.Sprintf("%s/board/%s/thread/%d", ApiURL, boardCode, threadID)
|
||||
body, err := c.makeRequest(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ошибка получения треда %d: %w", threadID, err)
|
||||
@ -182,7 +280,7 @@ func (c *Client) GetComments(boardCode string, threadID int) ([]models.Comment,
|
||||
return comments, nil
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/board/%s/thread/%d/comments", c.baseURL, boardCode, threadID)
|
||||
url := fmt.Sprintf("%s/board/%s/thread/%d/comments", ApiURL, boardCode, threadID)
|
||||
body, err := c.makeRequest(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ошибка получения комментариев треда %d: %w", threadID, err)
|
||||
@ -252,3 +350,133 @@ func (c *Client) GetFullThread(boardCode string, threadID int) (*models.ThreadDe
|
||||
|
||||
return thread, comments, nil
|
||||
}
|
||||
|
||||
func (c *Client) CreateThread(boardCode, title, text, passcode string) error {
|
||||
if passcode != "" {
|
||||
if err := c.LoginWithPasscode(passcode); err != nil {
|
||||
return fmt.Errorf("ошибка входа с passcode: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
formURL := fmt.Sprintf("%s/boards/board/%s/new", c.baseURL, boardCode)
|
||||
|
||||
resp, err := c.httpClient.Get(formURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка получения формы: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("ошибка получения формы: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка чтения формы: %w", err)
|
||||
}
|
||||
|
||||
csrfToken := extractCSRFToken(string(body))
|
||||
if csrfToken == "" {
|
||||
return fmt.Errorf("не удалось извлечь CSRF токен")
|
||||
}
|
||||
|
||||
formData := url.Values{}
|
||||
formData.Set("csrfmiddlewaretoken", csrfToken)
|
||||
formData.Set("title", title)
|
||||
formData.Set("text", text)
|
||||
|
||||
req, err := http.NewRequest("POST", formURL, bytes.NewBufferString(formData.Encode()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка создания запроса: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("Referer", formURL)
|
||||
|
||||
resp, err = c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка отправки: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusFound {
|
||||
return fmt.Errorf("ошибка сервера: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
cache.GetCache().Delete("threads_" + boardCode)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) AddComment(boardCode string, threadID int, text, passcode string) error {
|
||||
if passcode != "" {
|
||||
if err := c.LoginWithPasscode(passcode); err != nil {
|
||||
return fmt.Errorf("ошибка входа с passcode: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
formURL := fmt.Sprintf("%s/boards/board/%s/thread/%d/comment", c.baseURL, boardCode, threadID)
|
||||
|
||||
resp, err := c.httpClient.Get(formURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка получения формы: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("ошибка получения формы: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка чтения формы: %w", err)
|
||||
}
|
||||
|
||||
csrfToken := extractCSRFToken(string(body))
|
||||
if csrfToken == "" {
|
||||
return fmt.Errorf("не удалось извлечь CSRF токен")
|
||||
}
|
||||
|
||||
formData := url.Values{}
|
||||
formData.Set("csrfmiddlewaretoken", csrfToken)
|
||||
formData.Set("text", text)
|
||||
|
||||
req, err := http.NewRequest("POST", formURL, bytes.NewBufferString(formData.Encode()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка создания запроса: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("Referer", formURL)
|
||||
|
||||
resp, err = c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка отправки: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusFound {
|
||||
return fmt.Errorf("ошибка сервера: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("comments_%d", threadID)
|
||||
cache.GetCache().Delete(cacheKey)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractCSRFToken(html string) string {
|
||||
re := regexp.MustCompile(`name=['"]csrfmiddlewaretoken['"]\s+value=['"]([^'"]+)['"]`)
|
||||
matches := re.FindStringSubmatch(html)
|
||||
if len(matches) > 1 {
|
||||
return matches[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
8
cache/cache.go
vendored
8
cache/cache.go
vendored
@ -164,11 +164,11 @@ func (c *Cache) GetThreadsPage(boardCode string, page int) ([]models.Thread, boo
|
||||
|
||||
func (c *Cache) SetThreadDetail(threadID int, thread *models.ThreadDetail) {
|
||||
data, _ := json.Marshal(thread)
|
||||
c.Set("thread_"+string(rune(threadID)), data, 3*time.Minute)
|
||||
c.Set("thread_detail_"+fmt.Sprintf("%d", threadID), data, 3*time.Minute)
|
||||
}
|
||||
|
||||
func (c *Cache) GetThreadDetail(threadID int) (*models.ThreadDetail, bool) {
|
||||
data, exists := c.Get("thread_" + string(rune(threadID)))
|
||||
data, exists := c.Get("thread_detail_" + fmt.Sprintf("%d", threadID))
|
||||
if !exists {
|
||||
return nil, false
|
||||
}
|
||||
@ -184,11 +184,11 @@ func (c *Cache) GetThreadDetail(threadID int) (*models.ThreadDetail, bool) {
|
||||
|
||||
func (c *Cache) SetComments(threadID int, comments []models.Comment) {
|
||||
data, _ := json.Marshal(comments)
|
||||
c.Set("comments_"+string(rune(threadID)), data, 3*time.Minute)
|
||||
c.Set("comments_"+fmt.Sprintf("%d", threadID), data, 3*time.Minute)
|
||||
}
|
||||
|
||||
func (c *Cache) GetComments(threadID int) ([]models.Comment, bool) {
|
||||
data, exists := c.Get("comments_" + string(rune(threadID)))
|
||||
data, exists := c.Get("comments_" + fmt.Sprintf("%d", threadID))
|
||||
if !exists {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
19
main.go
19
main.go
@ -27,6 +27,25 @@ func main() {
|
||||
|
||||
apiClient := api.NewClient()
|
||||
|
||||
appSettings, err := settings.Load()
|
||||
if err == nil {
|
||||
if appSettings.Key != "" {
|
||||
if err := apiClient.Authenticate(appSettings.Key); err != nil {
|
||||
fmt.Printf("Ошибка аутентификации по ключу: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("Аутентификация по ключу успешна")
|
||||
}
|
||||
}
|
||||
|
||||
if appSettings.Passcode != "" {
|
||||
if err := apiClient.LoginWithPasscode(appSettings.Passcode); err != nil {
|
||||
fmt.Printf("Ошибка входа с passcode: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("Вход с passcode успешен")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uiManager := ui.NewUIManager(a, w, apiClient)
|
||||
|
||||
w.SetContent(uiManager.GetMainContent())
|
||||
|
||||
@ -18,6 +18,8 @@ type Settings struct {
|
||||
ShowFiles bool `json:"show_files"`
|
||||
CompactMode bool `json:"compact_mode"`
|
||||
PageSize int `json:"page_size"`
|
||||
Passcode string `json:"passcode"`
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
var settingsPath string
|
||||
|
||||
@ -4,11 +4,13 @@ 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 {
|
||||
@ -42,6 +44,7 @@ func NewUIManager(app fyne.App, window fyne.Window, apiClient *api.Client) *UIMa
|
||||
AutoRefresh: true,
|
||||
ShowFiles: true,
|
||||
CompactMode: false,
|
||||
Key: "",
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,6 +189,10 @@ func (ui *UIManager) ShowThreadDetail(board *models.Board, thread *models.Thread
|
||||
ui.ShowScreen(threadScreen)
|
||||
}
|
||||
|
||||
func (ui *UIManager) PushScreen(screen Screen) {
|
||||
ui.ShowScreen(screen)
|
||||
}
|
||||
|
||||
func (ui *UIManager) GetAPIClient() *api.Client {
|
||||
return ui.apiClient
|
||||
}
|
||||
@ -195,14 +202,15 @@ func (ui *UIManager) GetWindow() fyne.Window {
|
||||
}
|
||||
|
||||
func (ui *UIManager) ShowError(title, message string) {
|
||||
dialog := widget.NewModalPopUp(
|
||||
var dialog *widget.PopUp
|
||||
dialog = widget.NewModalPopUp(
|
||||
container.NewVBox(
|
||||
widget.NewLabel(title),
|
||||
widget.NewLabel(""),
|
||||
widget.NewLabel(message),
|
||||
widget.NewLabel(""),
|
||||
widget.NewButton("OK", func() {
|
||||
// Закрыть диалог - пока что просто прячем popup
|
||||
dialog.Hide()
|
||||
}),
|
||||
),
|
||||
ui.window.Canvas(),
|
||||
@ -211,14 +219,15 @@ func (ui *UIManager) ShowError(title, message string) {
|
||||
}
|
||||
|
||||
func (ui *UIManager) ShowInfo(title, message string) {
|
||||
dialog := widget.NewModalPopUp(
|
||||
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(),
|
||||
@ -226,6 +235,32 @@ func (ui *UIManager) ShowInfo(title, message string) {
|
||||
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
|
||||
}
|
||||
@ -234,6 +269,29 @@ 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()
|
||||
@ -243,6 +301,14 @@ 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))
|
||||
@ -251,7 +317,7 @@ func (ui *UIManager) ShowAbout() {
|
||||
widget.NewLabel("MobileMkch"),
|
||||
widget.NewLabel("Мобильный клиент для мкача"),
|
||||
widget.NewSeparator(),
|
||||
widget.NewLabel("Версия: 2.0.0-alpha (Always in alpha lol)"),
|
||||
widget.NewLabel("Версия: 3.0.0-alpha (Always in alpha lol)"),
|
||||
widget.NewLabel("Автор: w^x (лейн, платон, а похуй как угодно)"),
|
||||
widget.NewLabel("Разработано с ❤️ на Go + Fyne"),
|
||||
)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
@ -109,6 +110,76 @@ func (ss *SettingsScreen) setupContent() {
|
||||
lastBoardContainer := container.NewHBox(lastBoardLabel, lastBoardValue)
|
||||
ss.content.Add(lastBoardContainer)
|
||||
|
||||
ss.content.Add(widget.NewSeparator())
|
||||
|
||||
passcodeHeader := widget.NewLabel("Passcode")
|
||||
passcodeHeader.TextStyle = fyne.TextStyle{Bold: true}
|
||||
ss.content.Add(passcodeHeader)
|
||||
|
||||
passcodeEntry := widget.NewEntry()
|
||||
passcodeEntry.SetPlaceHolder("Введите passcode для постинга")
|
||||
passcodeEntry.Text = ss.uiManager.GetSettings().Passcode
|
||||
passcodeEntry.OnChanged = func(text string) {
|
||||
ss.uiManager.GetSettings().Passcode = text
|
||||
ss.uiManager.SaveSettings()
|
||||
}
|
||||
ss.content.Add(passcodeEntry)
|
||||
|
||||
keyHeader := widget.NewLabel("Ключ аутентификации")
|
||||
keyHeader.TextStyle = fyne.TextStyle{Bold: true}
|
||||
ss.content.Add(keyHeader)
|
||||
|
||||
keyEntry := widget.NewEntry()
|
||||
keyEntry.SetPlaceHolder("Введите ключ для аутентификации")
|
||||
keyEntry.Text = ss.uiManager.GetSettings().Key
|
||||
keyEntry.OnChanged = func(text string) {
|
||||
ss.uiManager.GetSettings().Key = text
|
||||
ss.uiManager.SaveSettings()
|
||||
}
|
||||
ss.content.Add(keyEntry)
|
||||
|
||||
authButtonsContainer := container.NewHBox()
|
||||
|
||||
testKeyButton := widget.NewButton("Тест ключа", func() {
|
||||
key := ss.uiManager.GetSettings().Key
|
||||
if key == "" {
|
||||
ss.uiManager.ShowError("Ошибка", "Ключ не введен")
|
||||
return
|
||||
}
|
||||
|
||||
err := ss.uiManager.GetAPIClient().Authenticate(key)
|
||||
if err != nil {
|
||||
ss.uiManager.ShowError("Ошибка аутентификации", fmt.Sprintf("Не удалось аутентифицироваться: %v", err))
|
||||
} else {
|
||||
ss.uiManager.ShowInfo("Успех", "Аутентификация по ключу прошла успешно")
|
||||
}
|
||||
})
|
||||
|
||||
testPasscodeButton := widget.NewButton("Тест passcode", func() {
|
||||
passcode := ss.uiManager.GetSettings().Passcode
|
||||
if passcode == "" {
|
||||
ss.uiManager.ShowError("Ошибка", "Passcode не введен")
|
||||
return
|
||||
}
|
||||
|
||||
err := ss.uiManager.GetAPIClient().LoginWithPasscode(passcode)
|
||||
if err != nil {
|
||||
ss.uiManager.ShowError("Ошибка passcode", fmt.Sprintf("Не удалось войти с passcode: %v", err))
|
||||
} else {
|
||||
ss.uiManager.ShowInfo("Успех", "Вход с passcode прошел успешно")
|
||||
}
|
||||
})
|
||||
|
||||
authButtonsContainer.Add(testKeyButton)
|
||||
authButtonsContainer.Add(testPasscodeButton)
|
||||
ss.content.Add(authButtonsContainer)
|
||||
|
||||
// Debug
|
||||
debugCheck := widget.NewCheck("Debug режим", func(checked bool) {
|
||||
ss.uiManager.GetAPIClient().EnableDebug(checked)
|
||||
})
|
||||
ss.content.Add(debugCheck)
|
||||
|
||||
ss.content.Add(widget.NewSeparator())
|
||||
cacheHeader := widget.NewLabel("Управление кэшем")
|
||||
cacheHeader.TextStyle = fyne.TextStyle{Bold: true}
|
||||
@ -166,6 +237,13 @@ func (ss *SettingsScreen) setupContent() {
|
||||
})
|
||||
ss.content.Add(aboutButton)
|
||||
|
||||
ss.content.Add(widget.NewSeparator())
|
||||
|
||||
infoButton := widget.NewButton("Я думаю тебя направили сюда)", func() {
|
||||
ss.uiManager.ShowInfoDialog()
|
||||
})
|
||||
ss.content.Add(infoButton)
|
||||
|
||||
ss.content.Refresh()
|
||||
}
|
||||
|
||||
|
||||
@ -141,6 +141,22 @@ func (tds *ThreadDetailScreen) displayThreadDetail() {
|
||||
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)
|
||||
|
||||
|
||||
@ -140,6 +140,16 @@ func (ts *ThreadsScreen) displayThreads() {
|
||||
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()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user