♻️ Refactor logger system

This commit is contained in:
2025-09-28 00:39:17 +08:00
parent 42bd7f97cb
commit fffca4a78c
28 changed files with 278 additions and 355 deletions

View File

@@ -1,11 +1,11 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer' as developer;
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart';
import 'package:island/pods/network.dart';
import 'package:island/talker.dart';
import 'package:island/widgets/account/status.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
@@ -69,13 +69,13 @@ class ActivityRpcServer {
// Start WebSocket server
while (port <= portRange[1]) {
developer.log('Trying port $port', name: kRpcLogPrefix);
talker.log('[$kRpcLogPrefix] Trying port $port');
try {
_httpServer = await HttpServer.bind(InternetAddress.loopbackIPv4, port);
developer.log('Listening on $port', name: kRpcLogPrefix);
talker.log('[$kRpcLogPrefix] Listening on $port');
shelf_io.serveRequests(_httpServer!, (Request request) async {
developer.log('New request', name: kRpcLogPrefix);
talker.log('[$kRpcLogPrefix] New request');
if (request.headers['upgrade']?.toLowerCase() == 'websocket') {
final handler = webSocketHandler((WebSocketChannel channel, _) {
_wsSockets.add(channel);
@@ -83,19 +83,16 @@ class ActivityRpcServer {
});
return handler(request);
}
developer.log(
'New request disposed due to not websocket',
name: kRpcLogPrefix,
);
talker.log('New request disposed due to not websocket');
return Response.notFound('Not a WebSocket request');
});
wsSuccess = true;
break;
} catch (e) {
if (e is SocketException && e.osError?.errorCode == 98) {
developer.log('$port in use!', name: kRpcLogPrefix);
talker.log('[$kRpcLogPrefix] $port in use!');
} else {
developer.log('HTTP error: $e', name: kRpcLogPrefix);
talker.log('[$kRpcLogPrefix] HTTP error: $e');
}
port++;
await Future.delayed(Duration(milliseconds: 100)); // Add delay
@@ -121,13 +118,10 @@ class ActivityRpcServer {
await _ipcServer!.start();
} catch (e) {
developer.log('IPC server error: $e', name: kRpcIpcLogPrefix);
talker.log('[$kRpcLogPrefix] IPC server error: $e');
}
} else {
developer.log(
'IPC server disabled on macOS or web in production mode',
name: kRpcIpcLogPrefix,
);
talker.log('IPC server disabled on macOS or web in production mode');
}
}
@@ -138,7 +132,7 @@ class ActivityRpcServer {
try {
await socket.sink.close();
} catch (e) {
developer.log('Error closing WebSocket: $e', name: kRpcLogPrefix);
talker.log('[$kRpcLogPrefix] Error closing WebSocket: $e');
}
}
_wsSockets.clear();
@@ -147,7 +141,7 @@ class ActivityRpcServer {
// Stop IPC server
await _ipcServer?.stop();
developer.log('Servers stopped', name: kRpcLogPrefix);
talker.log('[$kRpcLogPrefix] Servers stopped');
}
// Handle new WebSocket connection
@@ -159,10 +153,7 @@ class ActivityRpcServer {
final clientId = params['client_id'] ?? '';
final origin = request.headers['origin'] ?? '';
developer.log(
'New WS connection! origin: $origin, params: $params',
name: kRpcLogPrefix,
);
talker.log('New WS connection! origin: $origin, params: $params');
if (origin.isNotEmpty &&
![
@@ -170,22 +161,19 @@ class ActivityRpcServer {
'https://ptb.discord.com',
'https://canary.discord.com',
].contains(origin)) {
developer.log('Disallowed origin: $origin', name: kRpcLogPrefix);
talker.log('[$kRpcLogPrefix] Disallowed origin: $origin');
socket.sink.close();
return;
}
if (encoding != 'json') {
developer.log(
'Unsupported encoding requested: $encoding',
name: kRpcLogPrefix,
);
talker.log('Unsupported encoding requested: $encoding');
socket.sink.close();
return;
}
if (ver != 1) {
developer.log('Unsupported version requested: $ver', name: kRpcLogPrefix);
talker.log('[$kRpcLogPrefix] Unsupported version requested: $ver');
socket.sink.close();
return;
}
@@ -195,10 +183,10 @@ class ActivityRpcServer {
socket.stream.listen(
(data) => _onWsMessage(socketWithMeta, data),
onError: (e) {
developer.log('WS socket error: $e', name: kRpcLogPrefix);
talker.log('[$kRpcLogPrefix] WS socket error: $e');
},
onDone: () {
developer.log('WS socket closed', name: kRpcLogPrefix);
talker.log('[$kRpcLogPrefix] WS socket closed');
handlers['close']?.call(socketWithMeta);
_wsSockets.remove(socket);
},
@@ -210,25 +198,19 @@ class ActivityRpcServer {
// Handle incoming WebSocket message
Future<void> _onWsMessage(_WsSocketWrapper socket, dynamic data) async {
if (data is! String) {
developer.log(
'Invalid WebSocket message: not a string',
name: kRpcLogPrefix,
);
talker.log('Invalid WebSocket message: not a string');
return;
}
try {
final jsonData = await compute(jsonDecode, data);
if (jsonData is! Map<String, dynamic>) {
developer.log(
'Invalid WebSocket message: not a JSON object',
name: kRpcLogPrefix,
);
talker.log('Invalid WebSocket message: not a JSON object');
return;
}
developer.log('WS message: $jsonData', name: kRpcLogPrefix);
talker.log('[$kRpcLogPrefix] WS message: $jsonData');
handlers['message']?.call(socket, jsonData);
} catch (e) {
developer.log('WS message parse error: $e', name: kRpcLogPrefix);
talker.log('[$kRpcLogPrefix] WS message parse error: $e');
}
}
@@ -236,12 +218,12 @@ class ActivityRpcServer {
void _handleIpcPacket(IpcSocketWrapper socket, IpcPacket packet) {
switch (packet.type) {
case IpcTypes.ping:
developer.log('IPC ping received', name: kRpcIpcLogPrefix);
talker.log('[$kRpcLogPrefix] IPC ping received');
socket.sendPong(packet.data);
break;
case IpcTypes.pong:
developer.log('IPC pong received', name: kRpcIpcLogPrefix);
talker.log('[$kRpcLogPrefix] IPC pong received');
break;
case IpcTypes.handshake:
@@ -256,7 +238,7 @@ class ActivityRpcServer {
if (!socket.handshook) {
throw Exception('Need to handshake first');
}
developer.log('IPC frame: ${packet.data}', name: kRpcIpcLogPrefix);
talker.log('[$kRpcLogPrefix] IPC frame: ${packet.data}');
handlers['message']?.call(socket, packet.data);
break;
@@ -271,22 +253,19 @@ class ActivityRpcServer {
// Handle IPC handshake
void _onIpcHandshake(IpcSocketWrapper socket, Map<String, dynamic> params) {
developer.log('IPC handshake: $params', name: kRpcIpcLogPrefix);
talker.log('[$kRpcLogPrefix] IPC handshake: $params');
final ver = int.tryParse(params['v']?.toString() ?? '1') ?? 1;
final clientId = params['client_id']?.toString() ?? '';
if (ver != 1) {
developer.log(
'IPC unsupported version requested: $ver',
name: kRpcIpcLogPrefix,
);
talker.log('IPC unsupported version requested: $ver');
socket.closeWithCode(IpcErrorCodes.invalidVersion);
return;
}
if (clientId.isEmpty) {
developer.log('IPC client ID required', name: kRpcIpcLogPrefix);
talker.log('[$kRpcLogPrefix] IPC client ID required');
socket.closeWithCode(IpcErrorCodes.invalidClientId);
return;
}
@@ -306,7 +285,7 @@ class _WsSocketWrapper {
_WsSocketWrapper(this.channel, this.clientId, this.encoding);
void send(Map<String, dynamic> msg) {
developer.log('WS sending: $msg', name: kRpcLogPrefix);
talker.log('[$kRpcLogPrefix] WS sending: $msg');
channel.sink.add(jsonEncode(msg));
}
}
@@ -398,12 +377,7 @@ final rpcServerStateProvider =
final appId = socket.clientId;
final meta = data['args']['activity'];
try {
await setRemoteActivityStatus(
ref,
label,
appId,
meta,
);
await setRemoteActivityStatus(ref, label, appId, meta);
final now = DateTime.now();
final status = SnAccountStatus(
id: 'local_$appId',
@@ -422,10 +396,7 @@ final rpcServerStateProvider =
);
ref.read(currentAccountStatusProvider.notifier).setStatus(status);
} catch (e) {
developer.log(
'Failed to set remote activity status: $e',
name: kRpcLogPrefix,
);
talker.log('Failed to set remote activity status: $e');
}
socket.send({
'cmd': 'SET_ACTIVITY',
@@ -442,10 +413,7 @@ final rpcServerStateProvider =
await unsetRemoteActivityStatus(ref, appId);
ref.read(currentAccountStatusProvider.notifier).clearStatus();
} catch (e) {
developer.log(
'Failed to unset remote activity status: $e',
name: kRpcLogPrefix,
);
talker.log('Failed to unset remote activity status: $e');
}
},
});

View File

@@ -1,9 +1,9 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer' as developer;
import 'dart:io';
import 'dart:typed_data';
import 'package:dart_ipc/dart_ipc.dart';
import 'package:island/talker.dart';
import 'package:path/path.dart' as path;
const String kRpcIpcLogPrefix = 'arRPC.ipc';
@@ -128,23 +128,18 @@ class MultiPlatformIpcServer extends IpcServer {
@override
Future<void> start() async {
try {
final ipcPath = Platform.isWindows
? r'\\.\pipe\discord-ipc-0'
: await _findAvailableUnixIpcPath();
final ipcPath =
Platform.isWindows
? r'\\.\pipe\discord-ipc-0'
: await _findAvailableUnixIpcPath();
final serverSocket = await bind(ipcPath);
developer.log(
'IPC listening at $ipcPath',
name: kRpcIpcLogPrefix,
);
talker.log('IPC listening at $ipcPath');
_serverSubscription = serverSocket.listen((socket) {
final socketWrapper = MultiPlatformIpcSocketWrapper(socket);
addSocket(socketWrapper);
developer.log(
'New IPC connection!',
name: kRpcIpcLogPrefix,
);
talker.log('New IPC connection!');
_handleIpcData(socketWrapper);
});
} catch (e) {
@@ -158,7 +153,7 @@ class MultiPlatformIpcServer extends IpcServer {
try {
socket.close();
} catch (e) {
developer.log('Error closing IPC socket: $e', name: kRpcIpcLogPrefix);
talker.log('Error closing IPC socket: $e');
}
}
sockets.clear();
@@ -168,31 +163,30 @@ class MultiPlatformIpcServer extends IpcServer {
// Handle incoming IPC data
void _handleIpcData(MultiPlatformIpcSocketWrapper socket) {
final startTime = DateTime.now();
socket.socket.listen((data) {
final readStart = DateTime.now();
socket.addData(data);
final readDuration = DateTime.now().difference(readStart).inMicroseconds;
developer.log(
'Read data took $readDuration microseconds',
name: kRpcIpcLogPrefix,
);
socket.socket.listen(
(data) {
final readStart = DateTime.now();
socket.addData(data);
final readDuration =
DateTime.now().difference(readStart).inMicroseconds;
talker.log('Read data took $readDuration microseconds');
final packets = socket.readPackets();
for (final packet in packets) {
handlePacket?.call(socket, packet, {});
}
}, onDone: () {
developer.log('IPC connection closed', name: kRpcIpcLogPrefix);
socket.close();
}, onError: (e) {
developer.log('IPC data error: $e', name: kRpcIpcLogPrefix);
socket.closeWithCode(IpcCloseCodes.closeUnsupported, e.toString());
});
final totalDuration = DateTime.now().difference(startTime).inMicroseconds;
developer.log(
'_handleIpcData took $totalDuration microseconds',
name: kRpcIpcLogPrefix,
final packets = socket.readPackets();
for (final packet in packets) {
handlePacket?.call(socket, packet, {});
}
},
onDone: () {
talker.log('IPC connection closed');
socket.close();
},
onError: (e) {
talker.log('IPC data error: $e');
socket.closeWithCode(IpcCloseCodes.closeUnsupported, e.toString());
},
);
final totalDuration = DateTime.now().difference(startTime).inMicroseconds;
talker.log('_handleIpcData took $totalDuration microseconds');
}
Future<String> _getMacOsSystemTmpDir() async {
@@ -212,10 +206,7 @@ class MultiPlatformIpcServer extends IpcServer {
baseDirs.add(macTempDir);
}
} catch (e) {
developer.log(
'Failed to get macOS system temp dir: $e',
name: kRpcIpcLogPrefix,
);
talker.log('Failed to get macOS system temp dir: $e');
}
}
@@ -241,17 +232,11 @@ class MultiPlatformIpcServer extends IpcServer {
try {
await File(socketPath).delete();
} catch (_) {}
developer.log(
'IPC socket will be created at: $socketPath',
name: kRpcIpcLogPrefix,
);
talker.log('IPC socket will be created at: $socketPath');
return socketPath;
} catch (e) {
if (i == 0) {
developer.log(
'IPC path $socketPath not available: $e',
name: kRpcIpcLogPrefix,
);
talker.log('IPC path $socketPath not available: $e');
}
continue;
}
@@ -271,7 +256,7 @@ class MultiPlatformIpcSocketWrapper extends IpcSocketWrapper {
@override
void send(Map<String, dynamic> msg) {
developer.log('IPC sending: $msg', name: kRpcIpcLogPrefix);
talker.log('IPC sending: $msg');
final packet = IpcServer.encodeIpcPacket(IpcTypes.frame, msg);
socket.add(packet);
}

View File

@@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
@@ -10,6 +9,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:island/pods/network.dart';
import 'package:island/models/chat.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:island/talker.dart';
part 'call.g.dart';
part 'call.freezed.dart';
@@ -212,7 +212,7 @@ class CallNotifier extends _$CallNotifier {
Future<void> joinRoom(String roomId) async {
if (_roomId == roomId && _room != null) {
log('[Call] Call skipped. Already has data');
talker.info('[Call] Call skipped. Already has data');
return;
} else if (_room != null) {
if (!_room!.isDisposed &&

View File

@@ -6,7 +6,7 @@ part of 'call.dart';
// RiverpodGenerator
// **************************************************************************
String _$callNotifierHash() => r'eb9bd41b97e9b5e9d54007c8327edb6567458846';
String _$callNotifierHash() => r'd374402e51d331cf40724e51fd86bce3c5504776';
/// See also [CallNotifier].
@ProviderFor(CallNotifier)

View File

@@ -7,7 +7,7 @@ part of 'chat_online_count.dart';
// **************************************************************************
String _$chatOnlineCountNotifierHash() =>
r'254ed141ffd99585d898203b3d2b86c4d18db80d';
r'19af8fd0e9f62c65e12a68215406776085235fa3';
/// Copied from Dart SDK
class _SystemHash {

View File

@@ -7,7 +7,7 @@ part of 'chat_subscribe.dart';
// **************************************************************************
String _$chatSubscribeNotifierHash() =>
r'10a6b2c687149ebb419e4c96349d8bab1f183ec6';
r'df65ecf15d0e97d7e6850ac57b4e681606e77179';
/// Copied from Dart SDK
class _SystemHash {

View File

@@ -1,5 +1,4 @@
import "dart:async";
import "dart:developer" as developer;
import "package:dio/dio.dart";
import "package:drift/drift.dart" show Variable;
import "package:easy_localization/easy_localization.dart";
@@ -13,6 +12,7 @@ import "package:island/pods/database.dart";
import "package:island/pods/lifecycle.dart";
import "package:island/pods/network.dart";
import "package:island/services/file.dart";
import "package:island/talker.dart";
import "package:island/widgets/alert.dart";
import "package:riverpod_annotation/riverpod_annotation.dart";
import "package:uuid/uuid.dart";
@@ -60,10 +60,7 @@ class MessagesNotifier extends _$MessagesNotifier {
_identity = identity;
}
developer.log(
'MessagesNotifier built for room $roomId',
name: 'MessagesNotifier',
);
talker.log('MessagesNotifier built for room $roomId');
// Only setup sync and lifecycle listeners if user is a member
if (identity != null) {
@@ -71,24 +68,15 @@ class MessagesNotifier extends _$MessagesNotifier {
next.whenData((state) {
if (state == AppLifecycleState.paused) {
_lastPauseTime = DateTime.now();
developer.log(
'App paused, recording time',
name: 'MessagesNotifier',
);
talker.log('App paused, recording time');
} else if (state == AppLifecycleState.resumed) {
if (_lastPauseTime != null) {
final diff = DateTime.now().difference(_lastPauseTime!);
if (diff > const Duration(minutes: 1)) {
developer.log(
'App resumed after >1 min, syncing messages',
name: 'MessagesNotifier',
);
talker.log('App resumed after >1 min, syncing messages');
syncMessages();
} else {
developer.log(
'App resumed within 1 min, skipping sync',
name: 'MessagesNotifier',
);
talker.log('App resumed within 1 min, skipping sync');
}
}
}
@@ -109,10 +97,7 @@ class MessagesNotifier extends _$MessagesNotifier {
int offset = 0,
int take = 20,
}) async {
developer.log(
'Getting cached messages from offset $offset, take $take',
name: 'MessagesNotifier',
);
talker.log('Getting cached messages from offset $offset, take $take');
final List<LocalChatMessage> dbMessages;
if (_searchQuery != null && _searchQuery!.isNotEmpty) {
dbMessages = await _database.searchMessages(
@@ -174,10 +159,7 @@ class MessagesNotifier extends _$MessagesNotifier {
int offset = 0,
int take = 20,
}) async {
developer.log(
'Fetching messages from API, offset $offset, take $take',
name: 'MessagesNotifier',
);
talker.log('Fetching messages from API, offset $offset, take $take');
if (_totalCount == null) {
final response = await _apiClient.get(
'/sphere/chat/$_roomId/messages',
@@ -221,15 +203,12 @@ class MessagesNotifier extends _$MessagesNotifier {
Future<void> syncMessages() async {
if (_isSyncing) {
developer.log(
'Sync already in progress, skipping.',
name: 'MessagesNotifier',
);
talker.log('Sync already in progress, skipping.');
return;
}
_isSyncing = true;
developer.log('Starting message sync', name: 'MessagesNotifier');
talker.log('Starting message sync');
Future.microtask(() => ref.read(isSyncingProvider.notifier).state = true);
try {
final dbMessages = await _database.getMessagesForRoom(
@@ -243,10 +222,7 @@ class MessagesNotifier extends _$MessagesNotifier {
: _database.companionToMessage(dbMessages.first);
if (lastMessage == null) {
developer.log(
'No local messages, fetching from network',
name: 'MessagesNotifier',
);
talker.log('No local messages, fetching from network');
final newMessages = await _fetchAndCacheMessages(
offset: 0,
take: _pageSize,
@@ -264,10 +240,7 @@ class MessagesNotifier extends _$MessagesNotifier {
);
final response = MessageSyncResponse.fromJson(resp.data);
developer.log(
'Sync response: ${response.messages.length} changes',
name: 'MessagesNotifier',
);
talker.log('Sync response: ${response.messages.length} changes');
for (final message in response.messages) {
switch (message.type) {
case "messages.update":
@@ -282,15 +255,14 @@ class MessagesNotifier extends _$MessagesNotifier {
await receiveMessage(message);
}
} catch (err, stackTrace) {
developer.log(
talker.log(
'Error syncing messages',
name: 'MessagesNotifier',
error: err,
exception: err,
stackTrace: stackTrace,
);
showErrorAlert(err);
} finally {
developer.log('Finished message sync', name: 'MessagesNotifier');
talker.log('Finished message sync');
Future.microtask(
() => ref.read(isSyncingProvider.notifier).state = false,
);
@@ -340,7 +312,7 @@ class MessagesNotifier extends _$MessagesNotifier {
}
Future<void> loadInitial() async {
developer.log('Loading initial messages', name: 'MessagesNotifier');
talker.log('Loading initial messages');
if (_searchQuery == null || _searchQuery!.isEmpty) {
syncMessages();
}
@@ -354,7 +326,7 @@ class MessagesNotifier extends _$MessagesNotifier {
Future<void> loadMore() async {
if (!_hasMore || state is AsyncLoading) return;
developer.log('Loading more messages', name: 'MessagesNotifier');
talker.log('Loading more messages');
try {
final currentMessages = state.value ?? [];
@@ -370,10 +342,10 @@ class MessagesNotifier extends _$MessagesNotifier {
_sortMessages([...currentMessages, ...newMessages]),
);
} catch (err, stackTrace) {
developer.log(
talker.log(
'Error loading more messages',
name: 'MessagesNotifier',
error: err,
exception: err,
stackTrace: stackTrace,
);
showErrorAlert(err);
@@ -389,10 +361,7 @@ class MessagesNotifier extends _$MessagesNotifier {
Function(String, Map<int, double>)? onProgress,
}) async {
final nonce = const Uuid().v4();
developer.log(
'Sending message with nonce $nonce',
name: 'MessagesNotifier',
);
talker.log('Sending message with nonce $nonce');
final baseUrl = ref.read(serverUrlProvider);
final token = await getToken(ref.watch(tokenProvider));
if (token == null) throw ArgumentError('Access token is null');
@@ -496,15 +465,12 @@ class MessagesNotifier extends _$MessagesNotifier {
}).toList();
state = AsyncValue.data(newMessages);
}
developer.log(
'Message with nonce $nonce sent successfully',
name: 'MessagesNotifier',
);
talker.log('Message with nonce $nonce sent successfully');
} catch (e, stackTrace) {
developer.log(
talker.log(
'Failed to send message with nonce $nonce',
name: 'MessagesNotifier',
error: e,
exception: e,
stackTrace: stackTrace,
);
localMessage.status = MessageStatus.failed;
@@ -526,10 +492,7 @@ class MessagesNotifier extends _$MessagesNotifier {
}
Future<void> retryMessage(String pendingMessageId) async {
developer.log(
'Retrying message $pendingMessageId',
name: 'MessagesNotifier',
);
talker.log('Retrying message $pendingMessageId');
final message = await fetchMessageById(pendingMessageId);
if (message == null) {
throw Exception('Message not found');
@@ -573,10 +536,10 @@ class MessagesNotifier extends _$MessagesNotifier {
}).toList();
state = AsyncValue.data(newMessages);
} catch (e, stackTrace) {
developer.log(
talker.log(
'Failed to retry message $pendingMessageId',
name: 'MessagesNotifier',
error: e,
exception: e,
stackTrace: stackTrace,
);
message.status = MessageStatus.failed;
@@ -599,10 +562,7 @@ class MessagesNotifier extends _$MessagesNotifier {
Future<void> receiveMessage(SnChatMessage remoteMessage) async {
if (remoteMessage.chatRoomId != _roomId) return;
developer.log(
'Received new message ${remoteMessage.id}',
name: 'MessagesNotifier',
);
talker.log('Received new message ${remoteMessage.id}');
final localMessage = LocalChatMessage.fromRemoteMessage(
remoteMessage,
@@ -647,10 +607,7 @@ class MessagesNotifier extends _$MessagesNotifier {
Future<void> receiveMessageUpdate(SnChatMessage remoteMessage) async {
if (remoteMessage.chatRoomId != _roomId) return;
developer.log(
'Received message update ${remoteMessage.id}',
name: 'MessagesNotifier',
);
talker.log('Received message update ${remoteMessage.id}');
final targetId = remoteMessage.meta['message_id'] ?? remoteMessage.id;
final updatedMessage = LocalChatMessage.fromRemoteMessage(
@@ -673,10 +630,7 @@ class MessagesNotifier extends _$MessagesNotifier {
}
Future<void> receiveMessageDeletion(String messageId) async {
developer.log(
'Received message deletion $messageId',
name: 'MessagesNotifier',
);
talker.log('Received message deletion $messageId');
_pendingMessages.remove(messageId);
final currentMessages = state.value ?? [];
@@ -713,15 +667,15 @@ class MessagesNotifier extends _$MessagesNotifier {
}
Future<void> deleteMessage(String messageId) async {
developer.log('Deleting message $messageId', name: 'MessagesNotifier');
talker.log('Deleting message $messageId');
try {
await _apiClient.delete('/sphere/chat/$_roomId/messages/$messageId');
await receiveMessageDeletion(messageId);
} catch (err, stackTrace) {
developer.log(
talker.log(
'Error deleting message $messageId',
name: 'MessagesNotifier',
error: err,
exception: err,
stackTrace: stackTrace,
);
showErrorAlert(err);
@@ -743,10 +697,7 @@ class MessagesNotifier extends _$MessagesNotifier {
}
Future<LocalChatMessage?> fetchMessageById(String messageId) async {
developer.log(
'Fetching message by id $messageId',
name: 'MessagesNotifier',
);
talker.log('Fetching message by id $messageId');
try {
final localMessage =
await (_database.select(_database.chatMessages)
@@ -773,24 +724,18 @@ class MessagesNotifier extends _$MessagesNotifier {
}
Future<int> jumpToMessage(String messageId) async {
developer.log(
'Starting jump to message $messageId',
name: 'MessagesNotifier',
);
talker.log('Starting jump to message $messageId');
if (_isJumping) {
developer.log(
'Jump already in progress, skipping',
name: 'MessagesNotifier',
);
talker.log('Jump already in progress, skipping');
return -1;
}
_isJumping = true;
try {
developer.log('Fetching message $messageId', name: 'MessagesNotifier');
talker.log('Fetching message $messageId');
final message = await fetchMessageById(messageId);
if (message == null) {
developer.log('Message $messageId not found', name: 'MessagesNotifier');
talker.log('Message $messageId not found');
showSnackBar('messageNotFound'.tr());
return -1;
}
@@ -801,16 +746,14 @@ class MessagesNotifier extends _$MessagesNotifier {
(m) => m.id == messageId,
);
if (existingIndex >= 0) {
developer.log(
talker.log(
'Message $messageId already in current state at index $existingIndex, jumping directly',
name: 'MessagesNotifier',
);
return existingIndex;
}
developer.log(
talker.log(
'Message $messageId not in current state, loading messages around it',
name: 'MessagesNotifier',
);
// Count messages newer than this one
@@ -828,10 +771,7 @@ class MessagesNotifier extends _$MessagesNotifier {
// Load messages around this position
final offset =
(newerCount - _pageSize ~/ 2).clamp(0, double.infinity).toInt();
developer.log(
'Loading messages with offset $offset, take $_pageSize',
name: 'MessagesNotifier',
);
talker.log('Loading messages with offset $offset, take $_pageSize');
final loadedMessages = await _getCachedMessages(
offset: offset,
take: _pageSize,
@@ -841,9 +781,8 @@ class MessagesNotifier extends _$MessagesNotifier {
final currentIds = currentMessages.map((m) => m.id).toSet();
final newMessages =
loadedMessages.where((m) => !currentIds.contains(m.id)).toList();
developer.log(
talker.log(
'Loaded ${loadedMessages.length} messages, ${newMessages.length} are new',
name: 'MessagesNotifier',
);
if (newMessages.isNotEmpty) {
@@ -858,19 +797,15 @@ class MessagesNotifier extends _$MessagesNotifier {
}
_sortMessages(uniqueMessages);
state = AsyncValue.data(uniqueMessages);
developer.log(
talker.log(
'Updated state with ${uniqueMessages.length} total messages',
name: 'MessagesNotifier',
);
}
final finalIndex = (state.value ?? []).indexWhere(
(m) => m.id == messageId,
);
developer.log(
'Final index for message $messageId is $finalIndex',
name: 'MessagesNotifier',
);
talker.log('Final index for message $messageId is $finalIndex');
return finalIndex;
} finally {
_isJumping = false;

View File

@@ -6,7 +6,7 @@ part of 'messages_notifier.dart';
// RiverpodGenerator
// **************************************************************************
String _$messagesNotifierHash() => r'4257c9b3792418e913d0bac3ef58e727314635af';
String _$messagesNotifierHash() => r'3aad1491b777570913f3867abd280fa59949b1f1';
/// Copied from Dart SDK
class _SystemHash {

View File

@@ -63,7 +63,9 @@ class _$AppSettingsCopyWithImpl<$Res>
final AppSettings _self;
final $Res Function(AppSettings) _then;
@override @pragma('vm:prefer-inline') $Res call({Object? autoTranslate = null,Object? dataSavingMode = null,Object? soundEffects = null,Object? aprilFoolFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = null,}) {
/// Create a copy of AppSettings
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? autoTranslate = null,Object? dataSavingMode = null,Object? soundEffects = null,Object? aprilFoolFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,}) {
return _then(_self.copyWith(
autoTranslate: null == autoTranslate ? _self.autoTranslate : autoTranslate // ignore: cast_nullable_to_non_nullable
as bool,dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable
@@ -78,7 +80,8 @@ as int?,windowSize: freezed == windowSize ? _self.windowSize : windowSize // ign
as Size?,windowOpacity: null == windowOpacity ? _self.windowOpacity : windowOpacity // ignore: cast_nullable_to_non_nullable
as double,defaultPoolId: freezed == defaultPoolId ? _self.defaultPoolId : defaultPoolId // ignore: cast_nullable_to_non_nullable
as String?,messageDisplayStyle: null == messageDisplayStyle ? _self.messageDisplayStyle : messageDisplayStyle // ignore: cast_nullable_to_non_nullable
as String,
as String,themeMode: freezed == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable
as String?,
));
}
@@ -160,10 +163,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, double windowOpacity, String? defaultPoolId, String messageDisplayStyle)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, double windowOpacity, String? defaultPoolId, String messageDisplayStyle, String? themeMode)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _AppSettings() when $default != null:
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.windowSize,_that.windowOpacity,_that.defaultPoolId,_that.messageDisplayStyle);case _:
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.windowSize,_that.windowOpacity,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode);case _:
return orElse();
}
@@ -181,10 +184,10 @@ return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_tha
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, double windowOpacity, String? defaultPoolId, String messageDisplayStyle) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, double windowOpacity, String? defaultPoolId, String messageDisplayStyle, String? themeMode) $default,) {final _that = this;
switch (_that) {
case _AppSettings():
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.windowSize,_that.windowOpacity,_that.defaultPoolId,_that.messageDisplayStyle);}
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.windowSize,_that.windowOpacity,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode);}
}
/// A variant of `when` that fallback to returning `null`
///
@@ -198,10 +201,10 @@ return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_tha
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, double windowOpacity, String? defaultPoolId, String messageDisplayStyle)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool autoTranslate, bool dataSavingMode, bool soundEffects, bool aprilFoolFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, String? customFonts, int? appColorScheme, Size? windowSize, double windowOpacity, String? defaultPoolId, String messageDisplayStyle, String? themeMode)? $default,) {final _that = this;
switch (_that) {
case _AppSettings() when $default != null:
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.windowSize,_that.windowOpacity,_that.defaultPoolId,_that.messageDisplayStyle);case _:
return $default(_that.autoTranslate,_that.dataSavingMode,_that.soundEffects,_that.aprilFoolFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.customFonts,_that.appColorScheme,_that.windowSize,_that.windowOpacity,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode);case _:
return null;
}
@@ -244,16 +247,16 @@ _$AppSettingsCopyWith<_AppSettings> get copyWith => __$AppSettingsCopyWithImpl<_
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettings&&(identical(other.autoTranslate, autoTranslate) || other.autoTranslate == autoTranslate)&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.aprilFoolFeatures, aprilFoolFeatures) || other.aprilFoolFeatures == aprilFoolFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettings&&(identical(other.autoTranslate, autoTranslate) || other.autoTranslate == autoTranslate)&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.aprilFoolFeatures, aprilFoolFeatures) || other.aprilFoolFeatures == aprilFoolFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode));
}
@override
int get hashCode => Object.hash(runtimeType,autoTranslate,dataSavingMode,soundEffects,aprilFoolFeatures,enterToSend,appBarTransparent,showBackgroundImage,customFonts,appColorScheme,windowSize,windowOpacity,defaultPoolId,messageDisplayStyle);
int get hashCode => Object.hash(runtimeType,autoTranslate,dataSavingMode,soundEffects,aprilFoolFeatures,enterToSend,appBarTransparent,showBackgroundImage,customFonts,appColorScheme,windowSize,windowOpacity,defaultPoolId,messageDisplayStyle,themeMode);
@override
String toString() {
return 'AppSettings(autoTranslate: $autoTranslate, dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, aprilFoolFeatures: $aprilFoolFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, customFonts: $customFonts, appColorScheme: $appColorScheme, windowSize: $windowSize, windowOpacity: $windowOpacity, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle)';
return 'AppSettings(autoTranslate: $autoTranslate, dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, aprilFoolFeatures: $aprilFoolFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, customFonts: $customFonts, appColorScheme: $appColorScheme, windowSize: $windowSize, windowOpacity: $windowOpacity, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode)';
}
@@ -281,7 +284,7 @@ class __$AppSettingsCopyWithImpl<$Res>
/// Create a copy of AppSettings
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? autoTranslate = null,Object? dataSavingMode = null,Object? soundEffects = null,Object? aprilFoolFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? autoTranslate = null,Object? dataSavingMode = null,Object? soundEffects = null,Object? aprilFoolFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,}) {
return _then(_AppSettings(
autoTranslate: null == autoTranslate ? _self.autoTranslate : autoTranslate // ignore: cast_nullable_to_non_nullable
as bool,dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable
@@ -296,8 +299,8 @@ as int?,windowSize: freezed == windowSize ? _self.windowSize : windowSize // ign
as Size?,windowOpacity: null == windowOpacity ? _self.windowOpacity : windowOpacity // ignore: cast_nullable_to_non_nullable
as double,defaultPoolId: freezed == defaultPoolId ? _self.defaultPoolId : defaultPoolId // ignore: cast_nullable_to_non_nullable
as String?,messageDisplayStyle: null == messageDisplayStyle ? _self.messageDisplayStyle : messageDisplayStyle // ignore: cast_nullable_to_non_nullable
as String,themeMode: null == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable
as String,
as String,themeMode: freezed == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable
as String?,
));
}

View File

@@ -7,7 +7,7 @@ part of 'config.dart';
// **************************************************************************
String _$appSettingsNotifierHash() =>
r'3b0967a39a375c664c3fd44cbee7936b8b2f5fec';
r'c3042f77067b5f36102f17277e174a756121ac74';
/// See also [AppSettingsNotifier].
@ProviderFor(AppSettingsNotifier)

View File

@@ -10,6 +10,8 @@ import 'package:package_info_plus/package_info_plus.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:island/models/auth.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:talker_dio_logger/talker_dio_logger.dart';
import 'package:island/talker.dart';
import 'config.dart';
@@ -75,7 +77,7 @@ final apiClientProvider = Provider<Dio>((ref) {
),
);
dio.interceptors.add(
dio.interceptors.addAll([
InterceptorsWrapper(
onRequest: (
RequestOptions options,
@@ -97,7 +99,15 @@ final apiClientProvider = Provider<Dio>((ref) {
return handler.next(options);
},
),
);
TalkerDioLogger(
talker: talker,
settings: const TalkerDioLoggerSettings(
printRequestHeaders: true,
printResponseHeaders: true,
printResponseMessage: true,
),
),
]);
return dio;
});

View File

@@ -1,5 +1,4 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io' show Platform;
import 'package:dio/dio.dart';
@@ -12,6 +11,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart';
import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart';
import 'package:island/talker.dart';
class UserInfoNotifier extends StateNotifier<AsyncValue<SnAccount?>> {
final Ref _ref;
@@ -21,7 +21,7 @@ class UserInfoNotifier extends StateNotifier<AsyncValue<SnAccount?>> {
Future<void> fetchUser() async {
final token = _ref.watch(tokenProvider);
if (token == null) {
log('[UserInfo] No token found, not going to fetch...');
talker.info('[UserInfo] No token found, not going to fetch...');
return;
}
try {
@@ -75,11 +75,10 @@ class UserInfoNotifier extends StateNotifier<AsyncValue<SnAccount?>> {
}
});
}
log(
talker.error(
"[UserInfo] Failed to fetch user info...",
name: 'UserInfoNotifier',
error: error,
stackTrace: stackTrace,
error,
stackTrace,
);
state = AsyncValue.data(null);
}

View File

@@ -1,6 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -8,6 +7,7 @@ import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:island/talker.dart';
part 'websocket.freezed.dart';
part 'websocket.g.dart';
@@ -64,7 +64,7 @@ class WebSocketService {
final url = '$baseUrl/ws'.replaceFirst('http', 'ws');
log('[WebSocket] Trying connecting to $url');
talker.info('[WebSocket] Trying connecting to $url');
try {
if (kIsWeb) {
_channel = WebSocketChannel.connect(Uri.parse('$url?tk=$token'));
@@ -88,24 +88,24 @@ class WebSocketService {
return;
}
_streamController.sink.add(packet);
log(
talker.info(
"[WebSocket] Received packet: ${packet.type} ${packet.errorMessage}",
);
if (packet.type == 'pong' && _heartbeatAt != null) {
var now = DateTime.now();
heartbeatDelay = now.difference(_heartbeatAt!);
log(
talker.info(
"[WebSocket] Server respond last heartbeat for ${heartbeatDelay!.inMilliseconds} ms",
);
}
},
onDone: () {
log('[WebSocket] Connection closed, attempting to reconnect...');
talker.info('[WebSocket] Connection closed, attempting to reconnect...');
_scheduleReconnect();
_statusStreamController.sink.add(WebSocketState.disconnected());
},
onError: (error) {
log('[WebSocket] Error occurred: $error, attempting to reconnect...');
talker.error('[WebSocket] Error occurred: $error, attempting to reconnect...');
_scheduleReconnect();
_statusStreamController.sink.add(
WebSocketState.error(error.toString()),
@@ -113,7 +113,7 @@ class WebSocketService {
},
);
} catch (err) {
log('[WebSocket] Failed to connect: $err');
talker.error('[WebSocket] Failed to connect: $err');
_scheduleReconnect();
}
}
@@ -135,7 +135,7 @@ class WebSocketService {
void _beatTheHeart() {
_heartbeatAt = DateTime.now();
log('[WebSocket] We\'re beating the heart! $_heartbeatAt');
talker.info('[WebSocket] We\'re beating the heart! $_heartbeatAt');
sendMessage(jsonEncode(WebSocketPacket(type: 'ping', data: null)));
}