Files
Warp/apps/mobile/lib/state.dart
T
tajniak81andClaude Opus 5.5 3356089c13 Android app (apps/mobile) with Flutter
apps/mobile is a Matrix client for Android on the famedly Matrix Dart SDK,
with vodozemac (compiled from Rust during the build) for end-to-end
encryption. It speaks the plain Client-Server API and uses the web
client's design tokens, fonts and symbol.

- Sign in by server name (.well-known), homeserver address or full user ID;
  sign up with the dummy and registration token stages. Sessions survive
  restarts: SQLite in private storage, credentials also in the Keystore,
  both excluded from backups and device transfers.
- Room list with unread and mention counts, invites, search and filters.
  Timeline with grouping, day separators, notices, history on scroll,
  links, images and files (authenticated media). Sending text, photos,
  camera shots and files, with Sending/Sent/Read/Not sent and Retry.
  Typing, receipts, deleting. New rooms and DMs, encrypted by default;
  joining by address; room info with members, invite, rename, topic,
  notification level, leave.
- Encryption: one-step setup behind a recovery key, verifying a new session
  with the recovery key (restoring the backup) or another session, emoji
  verification of people and sessions, reset. Undecryptable messages say
  why. Session list with verify and sign out.
- Notifications while running, and push through UnifiedPush (no Firebase):
  the distributor's endpoint becomes an event_id_only pusher, the gateway
  is discovered, and a push starts the app headless to fetch, decrypt and
  show the event.

Make targets: mobile (release APKs per ABI into bin/), mobile-run,
mobile-test. Kotlin incremental compilation is off (Windows, project and
pub cache on different drives), and Tink's JVM artifact is excluded in
favour of tink-android.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 22:45:06 +02:00

63 lines
2.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:matrix/matrix.dart';
/// The one Matrix client, created in main() before the app starts.
final clientProvider = Provider<Client>(
(ref) => throw UnimplementedError('overridden in main()'),
);
/// Signed in or not. Starts from the restored session.
final loginStateProvider = StreamProvider<LoginState>((ref) async* {
final client = ref.watch(clientProvider);
yield client.isLogged() ? LoginState.loggedIn : LoginState.loggedOut;
yield* client.onLoginStateChanged.stream;
});
/// Bumps on every sync. The SDK's objects change in place, so screens that
/// show rooms watch this and read the client again; that is the whole
/// trick that keeps them current.
final syncProvider = StreamProvider<int>((ref) async* {
final client = ref.watch(clientProvider);
var n = 0;
yield n;
await for (final _ in client.onSync.stream) {
yield ++n;
}
});
/// Whether the connection to the homeserver is working, from the sync
/// loop's status. Null until the first sync after start finishes.
final syncErrorProvider = StreamProvider<bool>((ref) async* {
final client = ref.watch(clientProvider);
await for (final status in client.onSyncStatus.stream) {
if (status.status == SyncStatus.error) yield true;
if (status.status == SyncStatus.finished) yield false;
}
});
/// The theme the user picked: the system's, light or dark.
class ThemeModeNotifier extends Notifier<ThemeMode> {
static const _key = 'warp.theme';
static const _storage = FlutterSecureStorage();
@override
ThemeMode build() {
_storage.read(key: _key).then((value) {
final mode = ThemeMode.values.where((m) => m.name == value).firstOrNull;
if (mode != null) state = mode;
});
return ThemeMode.system;
}
void set(ThemeMode mode) {
state = mode;
_storage.write(key: _key, value: mode.name);
}
}
final themeModeProvider = NotifierProvider<ThemeModeNotifier, ThemeMode>(
ThemeModeNotifier.new,
);