Skip to content

Phase 1 core messaging#1

Merged
darxfame merged 121 commits into
mainfrom
phase-1-core-messaging
Jun 2, 2026
Merged

Phase 1 core messaging#1
darxfame merged 121 commits into
mainfrom
phase-1-core-messaging

Conversation

@darxfame

@darxfame darxfame commented Jun 2, 2026

Copy link
Copy Markdown
Owner

No description provided.

darxfame and others added 30 commits May 25, 2026 23:54
…спользовать встроенное принятие лицензий
Darxfame and others added 28 commits May 30, 2026 09:38
- ContactDao.updateStatus() — UPDATE contacts SET status
- ChatDao.clearUnreadCount() — UPDATE chats SET unreadCount = 0
- ChatRepository: presence WS events → contactDao.updateStatus (ONLINE/AWAY/DND/INVISIBLE/OFFLINE)
- ChatRepository.clearUnreadCount() — публичный метод
- ChatViewModel.loadChat(): clearUnreadCount при открытии чата

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MessageDao: updateContent, markDeletedForEveryone, updateReactions
ChatDao: setPinned, setMuted, setArchived
ChatRepository WS handlers: MessageEdited→updateContent, MessageDeleted→delete,
  MessageDeletedForEveryone→markDeleted, MessageReaction→updateReactions
ChatRepository: setPinned/setMuted/setArchived методы
ChatViewModel: editMessage, deleteMessage, addReaction
ChatScreen: Edit/Delete/React в контекстном меню (длинный тап)

33 тестов проходят.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Проблема: шаг создания Release отсутствовал — APK загружался только
как artifact (вкладка Actions), а не в Releases.

Изменения:
- permissions: contents: write — необходимо для создания Release
- softprops/action-gh-release@v2 — создаёт/обновляет тег latest-debug
  при каждом push, prerelease=true, make_latest=false

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MessageDao: два LIKE-запроса (глобальный и внутри чата), LIMIT 50,
минимальная длина запроса 2 символа в репозитории.
FTS4 — следующая итерация после накопления сообщений.

33 тестов проходят.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6 документов как source of truth для будущих Claude-сессий:
- RETRO_VISION.md    — исследование JIMM 2005-2008, Design Vision, Color System
- RETRO_COMPONENTS.md — Compose-спецификации: ContactRow, Bubble, InputBar, Picker
- RETRO_ASSETS.md    — смайлики, иконки, логотип, звуки, скины
- RETRO_BACKEND_REQUIREMENTS.md — матрица Local/Backend, API presence/status-message
- RETRO_ROADMAP.md   — 8 фаз (Foundation→Polish), AC, риски, accessibility
- RETRO_PROGRESS.md  — трекер ~22% готово

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
….crypto:eddsa

- EciesCrypto: Ed25519Signer/Ed25519PublicKeyParameters из BouncyCastle вместо
  net.i2p.crypto.eddsa; поле ed25519SeedBytes (32-байт seed) вместо EdDSAPrivateKey
- EciesKeyStore: loadOrGenEd25519Seed() возвращает ByteArray вместо EdDSAPrivateKey;
  java.util.Base64 и Timber вместо android.util.*
- build.gradle.kts: удалена зависимость net.i2p.crypto:eddsa:0.3.0
- Wire-совместимость с iOS (Apple CryptoKit Curve25519.Signing = RFC 8032) сохранена

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…PIN, UI

## Новые файлы (сервисный слой)
- ProxyManager: SOCKS5/HTTP прокси-селектор для OkHttp (runtime switching)
- RelayConfigRepository: remote fetch + Ed25519 подпись + disk cache + bundled fallback (5 реле)
- SingBoxTransport: рефакторинг — использует RelayConfigRepository; шаблон libbox.aar готов
- PINVaultManager: PBKDF2-HMAC-SHA256 400k + AES/GCM vault (3 слота: real/decoy/wipe)
  формат идентичен iOS PINVault; pepper в Android Keystore
- PanicPINManager: управление режимами real/decoy/wipe; StateFlow lockState/mode
- BiometricManager: AES-256 ключ в AndroidKeyStore + BiometricPrompt CryptoObject pattern

## Новые файлы (UI)
- StealthSettingsScreen: sing-box toggle, статус, ручной прокси, обновление реле
- PINSettingsScreen: настройка PIN real/decoy/wipe через PanicPINManager
- ConnectionDiagnosticsScreen: статус транспорта + HTTP health probe

## Изменения в существующих
- AppModule: OkHttpClient теперь использует RcqProxySelector (динамический прокси)
- build.gradle.kts: удалена зависимость net.i2p.crypto:eddsa:0.3.0

TODO (следующая сессия): подключить StealthSettingsScreen/PINSettingsScreen/
ConnectionDiagnosticsScreen в RCQApp.kt NavHost + SettingsScreen navigation buttons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
— Добавить маршруты settings/stealth, settings/pin, settings/diagnostics
— Добавить секцию «Stealth Mode» в SettingsScreen с тремя пунктами
— Передать onNavigateTo* как callback-параметры (default-значения {})

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…бках

— BypassMode enum (OFF/AUTO/MANUAL), по умолчанию AUTO
— ProxyManager: reportFailure/reportSuccess, порог 3 ошибки → sing-box
— WebSocketService: вызывает reportSuccess/reportFailure из onOpen/onFailure
— RcqProxySelector.connectFailed → proxyManager.reportFailure()
— StealthSettingsScreen: SegmentedButton выбор режима, UI адаптируется
— ConnectionDiagnosticsScreen: HEAD → GET (фикс 405 на /health)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…с групп и прокси

— P1/P2: LocalRetroMode CompositionLocal + DataStore persistence (AppPrefsViewModel)
— P1: AmoledColors + HighContrastColors (WCAG AAA 7:1)
— P2: RCQTheme принимает retroMode/amoledTheme/highContrast; передаёт из MainActivity
— P3: StatusGroupedContactList со sticky headers Online→Away→Busy→Offline (Offline свёрнут)
— P4: deletedForEveryone placeholder + editedAt ✏ иконка в timestamp
— P6: Retro/AMOLED/HighContrast toggles в Settings, персистентны через DataStore
— fix(proxy): ProxyManager восстанавливает sing-box после рестарта процесса
— fix(groups): GroupBrowseViewModel ищет публичные группы на сервере (GET groups/browse)
  с debounce 400ms; добавлен endpoint browsePublicGroups в RCQApiService

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ость, статус-дот

— Metrics: аватар 44→36dp, rowVPad 6→5dp, добавлены dividerThick/navBarHeight
— Typography: все размеры уменьшены на 2-8sp под компактный QIP-стиль
— BottomNavBar: плоская панель 50dp с accent-линией сверху активного таба (без pill)
— ChatsScreen ChatItem: статус-дот слева + квадратный аватар + тонкий divider между строками
— ContactsScreen: статус-группировка всегда активна (убран флаг LocalRetroMode)
— ContactItem: статус-дот перемещён влево, квадратный аватар, monospace UIN italic статус
— SettingsScreen: удалены Card-карточки, плоские секции с uppercase accent-заголовками

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
— OkHttp timeout: 60s→10s connect/15s read/write, retryOnConnectionFailure=false
— Dark theme: сохраняется в DataStore (DARK_THEME), читается в AppPrefsViewModel+MainActivity
— SettingsViewModel.darkTheme теперь StateFlow из DataStore (было in-memory)
— JIMM retro toggle: восстановлен LocalRetroMode gate в ContactsScreen
— Входящие сообщения: TOFU-mismatch больше не дропает (warning), fallback senderUin
  из raw WS объекта, placeholder "🔒 Зашифрованное сообщение" при неудаче decrypt
— ChatRepository: senderId→senderUin, null-safe decrypted?.content/kind/messageId
— ProxyManager: статус AUTO переименован с "ожидаю ошибок" на понятный текст

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
— ProxyManager.probeAndAutoEnable(): HEAD /health за 4с, при ошибке
  включает AUTO-режим и стартует sing-box, колбэк onStatus для UI
— AuthViewModel: инжектирует ProxyManager, вызывает probeAndAutoEnable()
  в checkExistingAuth() до перехода в Authenticated/Onboarding,
  connectionStatus: StateFlow<String> транслирует статус пробы
— ConnectionProbeSplash: 8-лепестковый цветок ICQ (rotate animation),
  строка статуса обновляется в реальном времени ("Проверяю соединение…"
  → "Прямое подключение ✓" / "Включаю обход…" → "Обход включён")
— AuthNavigation: AuthState.Loading теперь показывает ConnectionProbeSplash

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
— BottomNavBar: добавлен navigationBarsPadding() — navbar больше не скрывается
  под Home/Back/Recents при gesture navigation
— GroupRepository.getGroups(): убран фильтр ownUin in memberIds — сервер
  GET /groups уже возвращает только группы пользователя, memberIds пуст
  когда сервер не передаёт массив members (bandwidth opt)
— ContactsViewModel.groups: аналогично убран memberIds filter

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ContactRepository.DEV_UIN = 100000001L: авто-добавление без заявки
- ContactItem: бейдж (.Dev) у контакта-разработчика (accent monospace)
- WebSocketService: добавлен RcqProxySelector → WS тоже идёт через обход
- DiagnosticsViewModel: инжект RCQApiService + WebSocketService
- ConnectionDiagnosticsScreen: 10 ручек: /health /users/me /contacts
  /contacts/pending /chats /messages/queue /settings /groups /stories /rooms
- Каждый тест с таймаутом 8s и отображением HTTP-кода и ms

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sage, haptic

- Room migration 13→14: contacts.statusMessage TEXT
- ContactEntity / Contact domain: поле statusMessage
- ChatRepository.toDomain / toEntity: прокидывают statusMessage
- ContactItem: показывает statusMessage под UIN (italic, secondary)
- LocalCompactMode CompositionLocal в Theme.kt (по образцу LocalRetroMode)
- RCQTheme: param compactMode + provides LocalCompactMode
- AppPrefsViewModel: compactMode StateFlow + setCompactMode()
- SettingsViewModel: compactMode StateFlow + setCompactMode()
- SettingsScreen: Compact Mode toggle в секции Appearance
- MainActivity: читает compactMode, передаёт в RCQTheme
- ChatScreen: RetroSystemMessage для MessageKind.SYSTEM_NOTICE
- ChatScreen: haptic feedback (HapticFeedbackType.LongPress) на кнопке Send
- DEV_UIN = 84048L (исправлен с 100000001)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…heme consistency

- WebSocketService: удалены 30 dead WsEvent (Games/Pets/Marketplace/Hood/Auction/Trade)
  и соответствующие parseEvent ветки — удалённые фичи out of scope
- AuthInterceptor.PreferencesKeys: +7 DataStore ключей для Settings toggles
- SettingsViewModel: Notifications/Sound/Vibration/ReadReceipts/LastSeen/Online
  теперь персистируются через DataStore (были in-memory MutableStateFlow)
- SettingsScreen TopAppBar/Header: Background/SurfaceVariant → rcq.bgPrimary/bgSecondary
  исправляет ломаный вид в AMOLED и HighContrast темах
- ChatScreen TopAppBar avatar: CircleShape → RoundedCornerShape(3dp), theme colors
- Документация: DB v13→v14, BUG-007 → resolved, Compact mode ✅, RETRO_PROGRESS

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…бход

Root-cause аудит против live api.rcq.app:

1. getCurrentUser() бил GET /users/me → 405 (это PUT-only эндпоинт).
   Правильный путь per spec — GET /users/me/info. Чинит загрузку профиля
   и резолв текущего пользователя в ChatRepository.

2. Поиск групп: GroupBrowseViewModel при поиске показывал ТОЛЬКО серверные
   результаты, игнорируя локальные группы. Теперь сливает substring-совпадения
   (contains, ignoreCase, в любом месте имени) по своим группам с серверной
   выдачей — "другую группу не находит" исправлено.

3. Диагностика: 401/403 помечались как сбой ✗, создавая "кучу ошибок",
   хотя соединение работает (нет только авторизации). Теперь 401/403 = "соединение OK",
   404/405 = "эндпоинт недоступен". Добавлена строка с активным доменом.
   Лейбл /users/me → /users/me/info.

4. Ручной обход: добавлен ProxyManager.forceEnableNow() + кнопка "Включить сейчас"
   в AUTO-режиме (StealthSettingsScreen) — не нужно ждать 3 ошибки.

Прим.: SingBox-маршрутизация всё ещё заглушка (BUG-005, нужен бинарник);
staging-домен api.staging.rcq.app не имеет DNS-записи (использовать production).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
  - SingBoxTransport: запуск libbox через reflection (BoxService(config).start()
    с фолбэком на Libbox.newService(config, platform)), no-op PlatformInterface
    для режима SOCKS5, честная остановка close()/stop(), фоновая проба реле
  - isEngineAvailable: проверка наличия ядра на classpath
  - ProxyManager: честная метка статуса вместо ложного «sing-box активен»
  - build.gradle.kts: подхват app/libs/*.aar через fileTree
  - .gitignore: app/libs/*.aar (бинарник 64МБ тянуть отдельно)
  - docs/SINGBOX_INTEGRATION.md: инструкция сборки/получения ядра

  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ltest — обход cache.db read-only

Корневая причина: Go-runtime libbox пишет cache.db в CWD, который на Android = '/' (read-only).
Флаг cache_file.enabled=false игнорировался ядром.

Решение (тройная защита):
1. Libbox.setup(SetupOptions) с basePath=filesDir, workingPath=filesDir, tempPath=cacheDir
   — переключает CWD Go-runtime на writable директорию ДО newService()/start()
2. Замена urltest → selector outbound — selector не требует cache.db для хранения
   истории проб; выбор быстрейшего relay делаем сами через TCP-probe + PREF_LAST_RELAY
3. experimental.cache_file.enabled=false — третий слой на случай если ядро всё-таки
   попытается открыть cache.db

Также: setFixAndroidStack(true) — чинит Go-stack под Android lifecycle.
- Removed product flavor configuration for staging build variant
- Keep only production flavor with correct https://api.rcq.app/ domain
- API now exclusively uses the working production endpoint
- Fixes DNS NXDOMAIN errors that were occurring with staging domain
- Keep only production flavor with https://api.rcq.app/
- Remove staging flavor with nonexistent api.staging.rcq.app domain
- Gradle now recognizes assembleProductionDebug task
- Fixes build failure due to missing flavor configuration
@darxfame darxfame merged commit 9380938 into main Jun 2, 2026
2 checks passed
@darxfame darxfame deleted the phase-1-core-messaging branch June 3, 2026 13:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant