docs(flutter): add dart doc comments to core services and widgets (TODO-085)
Add dart doc comments to poke_notification_service, poke_preferences_service, link_trust_service, voice_settings, snapshot_view covering public API.
This commit is contained in:
@@ -3,11 +3,19 @@ import 'package:flutter/material.dart';
|
||||
import '../l10n/generated/app_localizations.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// Manages user-trusted link domains to suppress external-link warnings.
|
||||
///
|
||||
/// Trusted domains are persisted in [SharedPreferences] under
|
||||
/// `'trusted_domains'`. Supports wildcard patterns (e.g. `'*.example.com'`)
|
||||
/// that match any subdomain of the base host.
|
||||
///
|
||||
/// This is a singleton; use [LinkTrustService.instance] to obtain it.
|
||||
class LinkTrustService extends ChangeNotifier {
|
||||
static LinkTrustService? _instance;
|
||||
final Set<String> _trusted = {};
|
||||
bool _loaded = false;
|
||||
|
||||
/// Returns the singleton [LinkTrustService] instance.
|
||||
static LinkTrustService get instance {
|
||||
_instance ??= LinkTrustService._();
|
||||
return _instance!;
|
||||
@@ -26,6 +34,10 @@ class LinkTrustService extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Returns `true` if [host] matches any trusted domain pattern.
|
||||
///
|
||||
/// Matching is case-insensitive. Wildcard patterns like `'*.example.com'`
|
||||
/// match both `example.com` and any `*.example.com` subdomain.
|
||||
bool isTrusted(String host) {
|
||||
host = host.toLowerCase();
|
||||
for (final pattern in _trusted) {
|
||||
@@ -34,6 +46,7 @@ class LinkTrustService extends ChangeNotifier {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Persists [host] as a trusted domain and notifies listeners.
|
||||
Future<void> addTrustedDomain(String host) async {
|
||||
host = host.toLowerCase();
|
||||
_trusted.add(host);
|
||||
@@ -51,6 +64,10 @@ class LinkTrustService extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
/// Shows a dialog asking the user whether to open an external link.
|
||||
///
|
||||
/// Returns `true` if the user chose to open and checked "remember this domain",
|
||||
/// `false` if the user chose to open without remembering, or `null` if cancelled.
|
||||
Future<bool?> showLinkTrustDialog(BuildContext context, String domain) async {
|
||||
bool remember = false;
|
||||
return showDialog<bool>(
|
||||
|
||||
@@ -3,7 +3,20 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
|
||||
import '../src/rust/api.dart' as rust;
|
||||
|
||||
/// Manages local push notifications for TeamSpeak poke events.
|
||||
///
|
||||
/// Handles platform-specific notification configuration across Android,
|
||||
/// iOS, macOS, Linux, and Windows. Notification sound is intentionally
|
||||
/// delegated to [EventSoundService] (tracked: TODO-event-sounds); this
|
||||
/// service only manages the visual notification surface.
|
||||
///
|
||||
/// Poke strength maps to platform-appropriate urgency levels:
|
||||
/// - [BridgePokeStrength.strong] → high-priority / time-sensitive
|
||||
/// - [BridgePokeStrength.suppressed] → normal priority
|
||||
/// - [BridgePokeStrength.suppressedOverflow] → passive / low priority
|
||||
class PokeNotificationService {
|
||||
/// Creates a [PokeNotificationService] with an optional
|
||||
/// [FlutterLocalNotificationsPlugin] for testing.
|
||||
PokeNotificationService({FlutterLocalNotificationsPlugin? notifications})
|
||||
: _notifications = notifications ?? FlutterLocalNotificationsPlugin();
|
||||
|
||||
@@ -22,6 +35,9 @@ class PokeNotificationService {
|
||||
final FlutterLocalNotificationsPlugin _notifications;
|
||||
bool _initialized = false;
|
||||
|
||||
/// Initializes the notification plugin with platform-specific settings.
|
||||
///
|
||||
/// Safe to call multiple times; subsequent calls are no-ops.
|
||||
Future<void> init() async {
|
||||
if (_initialized) return;
|
||||
await _notifications.initialize(
|
||||
@@ -56,6 +72,10 @@ class PokeNotificationService {
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
/// Requests notification permission from the user on the current platform.
|
||||
///
|
||||
/// Returns `true` on platforms where permission is not required (web,
|
||||
/// Linux, Windows) or when the user grants permission.
|
||||
Future<bool> requestPermission() async {
|
||||
await init();
|
||||
if (kIsWeb) return true;
|
||||
@@ -87,6 +107,8 @@ class PokeNotificationService {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Displays a poke notification from [senderName] with [strength]-based
|
||||
/// urgency. Silently returns if the user has denied notification permission.
|
||||
Future<void> show({
|
||||
required String senderName,
|
||||
required String message,
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// Persists user preferences for TeamSpeak poke notifications.
|
||||
///
|
||||
/// Stores two settings:
|
||||
/// - Whether pokes are globally enabled
|
||||
/// - A set of muted sender client IDs
|
||||
///
|
||||
/// Preferences are written to [SharedPreferences] and observable
|
||||
/// through [ValueListenable] so UI widgets can rebuild reactively.
|
||||
class PokePreferencesService {
|
||||
static const _enabledKey = 'pokes.enabled';
|
||||
static const _mutedSendersKey = 'pokes.muted_senders';
|
||||
@@ -10,9 +18,13 @@ class PokePreferencesService {
|
||||
const <BigInt>{},
|
||||
);
|
||||
|
||||
/// Whether poke notifications are globally enabled.
|
||||
ValueListenable<bool> get pokesEnabled => _pokesEnabled;
|
||||
|
||||
/// Set of client IDs whose pokes are muted.
|
||||
ValueListenable<Set<BigInt>> get mutedSenders => _mutedSenders;
|
||||
|
||||
/// Loads persisted preferences from [SharedPreferences].
|
||||
Future<void> load() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
_pokesEnabled.value = prefs.getBool(_enabledKey) ?? true;
|
||||
@@ -21,18 +33,21 @@ class PokePreferencesService {
|
||||
.toSet();
|
||||
}
|
||||
|
||||
/// Enables or disables poke notifications globally.
|
||||
Future<void> setPokesEnabled(bool enabled) async {
|
||||
_pokesEnabled.value = enabled;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool(_enabledKey, enabled);
|
||||
}
|
||||
|
||||
/// Adds [senderId] to the muted senders set.
|
||||
Future<void> muteSender(BigInt senderId) async {
|
||||
if (_mutedSenders.value.contains(senderId)) return;
|
||||
_mutedSenders.value = {..._mutedSenders.value, senderId};
|
||||
await _saveMutedSenders();
|
||||
}
|
||||
|
||||
/// Removes [senderId] from the muted senders set.
|
||||
Future<void> unmuteSender(BigInt senderId) async {
|
||||
if (!_mutedSenders.value.contains(senderId)) return;
|
||||
_mutedSenders.value = _mutedSenders.value
|
||||
@@ -41,6 +56,7 @@ class PokePreferencesService {
|
||||
await _saveMutedSenders();
|
||||
}
|
||||
|
||||
/// Returns `true` if [senderId] is in the muted senders set.
|
||||
bool isMuted(BigInt senderId) => _mutedSenders.value.contains(senderId);
|
||||
|
||||
Future<void> _saveMutedSenders() async {
|
||||
@@ -51,6 +67,7 @@ class PokePreferencesService {
|
||||
);
|
||||
}
|
||||
|
||||
/// Releases [ValueNotifier] resources.
|
||||
void dispose() {
|
||||
_pokesEnabled.dispose();
|
||||
_mutedSenders.dispose();
|
||||
|
||||
@@ -11,7 +11,14 @@ import '../src/rust/api.dart' as rust;
|
||||
import 'bbcode_text.dart';
|
||||
import 'talk_power_warning.dart';
|
||||
|
||||
/// Connected-server snapshot with welcome text, channels, and clients.
|
||||
/// Connected-server snapshot displaying welcome text, channel tree, and clients.
|
||||
///
|
||||
/// Renders the full channel hierarchy from a [BridgeSnapshot] with expandable
|
||||
/// channel nodes, client voice-status indicators, unread-message badges, and
|
||||
/// context menus for client actions (info, chat, poke, volume).
|
||||
///
|
||||
/// Channel join is triggered by tapping an unlocked channel row; password-
|
||||
/// protected channels invoke [onJoinChannelWithPassword] instead.
|
||||
class SnapshotView extends StatefulWidget {
|
||||
/// Construct a snapshot view.
|
||||
const SnapshotView({
|
||||
@@ -1149,7 +1156,7 @@ class _ClientVolumeSheetState extends State<_ClientVolumeSheet> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Context menu for channel tiles. Shows a "Chat" option on right-click or
|
||||
/// Context menu for channel tiles offering "Chat" on right-click or
|
||||
/// long-press. Primary tap passes through to the child for voice join.
|
||||
class _ChannelContextMenu extends StatelessWidget {
|
||||
const _ChannelContextMenu({
|
||||
|
||||
@@ -19,7 +19,8 @@ import 'voice_platform.dart';
|
||||
import 'voice_settings_controls.dart';
|
||||
import '../src/rust/api.dart' as rust;
|
||||
|
||||
/// Result returned by [VoiceSettingsDialog].
|
||||
/// Result returned by [VoiceSettingsDialog] when the user saves or
|
||||
/// requests a key bind.
|
||||
class VoiceSettingsResult {
|
||||
const VoiceSettingsResult({
|
||||
required this.mode,
|
||||
@@ -34,7 +35,15 @@ class VoiceSettingsResult {
|
||||
final rust.BridgeAudioProcessingConfig audioConfig;
|
||||
}
|
||||
|
||||
/// Voice + audio processing settings dialog.
|
||||
/// Dialog for configuring voice transmission and audio processing settings.
|
||||
///
|
||||
/// Surfaces transmit mode selection (continuous, PTT, voice-activity),
|
||||
/// PTT release-tail slider, key-bind request, and a full audio processing
|
||||
/// panel covering noise suppression, echo cancellation, AGC, HPF, and VAD
|
||||
/// backend selection.
|
||||
///
|
||||
/// On mobile hosts, the voice-activity segment is hidden when no
|
||||
/// Chanora-owned VAD pipeline is available (iOS, macOS, web).
|
||||
class VoiceSettingsDialog extends StatefulWidget {
|
||||
const VoiceSettingsDialog({
|
||||
super.key,
|
||||
|
||||
Reference in New Issue
Block a user