Fix online status not getting set correctly

Closes #6
This commit is contained in:
Nutcake 2023-10-12 18:01:15 +02:00
parent 023ed9edc1
commit fcd34d4fb1
9 changed files with 41 additions and 16 deletions

View file

@ -31,6 +31,13 @@ class RecordApi {
return body.map((e) => Record.fromMap(e)).toList(); return body.map((e) => Record.fromMap(e)).toList();
} }
static Future<List<Record>> getGroupRecordsAt(ApiClient client, {required String path, required String groupId}) async {
final response = await client.get("/users/$groupId/records?path=$path");
client.checkResponse(response);
final body = jsonDecode(response.body) as List;
return body.map((e) => Record.fromMap(e)).toList();
}
static Future<void> deleteRecord(ApiClient client, {required String recordId}) async { static Future<void> deleteRecord(ApiClient client, {required String recordId}) async {
final response = await client.delete("/users/${client.userId}/records/$recordId"); final response = await client.delete("/users/${client.userId}/records/$recordId");
client.checkResponse(response); client.checkResponse(response);

View file

@ -24,6 +24,7 @@ import 'package:package_info_plus/package_info_plus.dart';
class MessagingClient extends ChangeNotifier { class MessagingClient extends ChangeNotifier {
static const Duration _autoRefreshDuration = Duration(seconds: 10); static const Duration _autoRefreshDuration = Duration(seconds: 10);
static const Duration _unreadSafeguardDuration = Duration(seconds: 120); static const Duration _unreadSafeguardDuration = Duration(seconds: 120);
static const Duration _statusHeartbeatDuration = Duration(seconds: 150);
static const String _messageBoxKey = "message-box"; static const String _messageBoxKey = "message-box";
static const String _lastUpdateKey = "__last-update-time"; static const String _lastUpdateKey = "__last-update-time";
@ -38,7 +39,7 @@ class MessagingClient extends ChangeNotifier {
final Set<String> _knownSessionKeys = {}; final Set<String> _knownSessionKeys = {};
Friend? selectedFriend; Friend? selectedFriend;
Timer? _notifyOnlineTimer; Timer? _statusHeartbeat;
Timer? _autoRefresh; Timer? _autoRefresh;
Timer? _unreadSafeguard; Timer? _unreadSafeguard;
String? _initStatus; String? _initStatus;
@ -62,7 +63,7 @@ class MessagingClient extends ChangeNotifier {
void dispose() { void dispose() {
debugPrint("mClient disposed: $hashCode"); debugPrint("mClient disposed: $hashCode");
_autoRefresh?.cancel(); _autoRefresh?.cancel();
_notifyOnlineTimer?.cancel(); _statusHeartbeat?.cancel();
_unreadSafeguard?.cancel(); _unreadSafeguard?.cancel();
_hubManager.dispose(); _hubManager.dispose();
super.dispose(); super.dispose();
@ -117,6 +118,7 @@ class MessagingClient extends ChangeNotifier {
} }
void markMessagesRead(MarkReadBatch batch) { void markMessagesRead(MarkReadBatch batch) {
if (_userStatus.onlineStatus == OnlineStatus.invisible || _userStatus.onlineStatus == OnlineStatus.offline) return;
final msgBody = batch.toMap(); final msgBody = batch.toMap();
_hubManager.send("MarkMessagesRead", arguments: [msgBody]); _hubManager.send("MarkMessagesRead", arguments: [msgBody]);
clearUnreadsForUser(batch.senderId); clearUnreadsForUser(batch.senderId);
@ -124,11 +126,14 @@ class MessagingClient extends ChangeNotifier {
Future<void> setOnlineStatus(OnlineStatus status) async { Future<void> setOnlineStatus(OnlineStatus status) async {
final pkginfo = await PackageInfo.fromPlatform(); final pkginfo = await PackageInfo.fromPlatform();
final now = DateTime.now();
_userStatus = _userStatus.copyWith( _userStatus = _userStatus.copyWith(
userId: _apiClient.userId,
appVersion: "${pkginfo.version} of ${pkginfo.appName}", appVersion: "${pkginfo.version} of ${pkginfo.appName}",
lastStatusChange: DateTime.now(), lastPresenceTimestamp: now,
lastStatusChange: now,
onlineStatus: status, onlineStatus: status,
isPresent: true,
); );
_hubManager.send( _hubManager.send(
@ -258,7 +263,6 @@ class MessagingClient extends ChangeNotifier {
_hubManager.setHandler(EventTarget.removeSession, _onRemoveSession); _hubManager.setHandler(EventTarget.removeSession, _onRemoveSession);
await _hubManager.start(); await _hubManager.start();
await setOnlineStatus(OnlineStatus.online);
_hubManager.send( _hubManager.send(
"InitializeStatus", "InitializeStatus",
responseHandler: (Map data) async { responseHandler: (Map data) async {
@ -272,6 +276,10 @@ class MessagingClient extends ChangeNotifier {
await _refreshUnreads(); await _refreshUnreads();
_unreadSafeguard = Timer.periodic(_unreadSafeguardDuration, (timer) => _refreshUnreads()); _unreadSafeguard = Timer.periodic(_unreadSafeguardDuration, (timer) => _refreshUnreads());
_hubManager.send("RequestStatus", arguments: [null, false]); _hubManager.send("RequestStatus", arguments: [null, false]);
await setOnlineStatus(OnlineStatus.online);
_statusHeartbeat = Timer.periodic(_statusHeartbeatDuration, (timer) {
setOnlineStatus(_userStatus.onlineStatus);
});
}, },
); );
} }

View file

@ -107,10 +107,10 @@ class HubManager {
void _handleInvocation(body) async { void _handleInvocation(body) async {
final target = EventTarget.parse(body["target"]); final target = EventTarget.parse(body["target"]);
final args = body["arguments"] ?? []; final args = body["arguments"] ?? [];
if (kDebugMode) _logger.info("Invocation target: ${target.name}, args:\n$args");
final handler = _handlers[target]; final handler = _handlers[target];
if (handler == null) { if (handler == null) {
_logger.info("Unhandled event received"); _logger.warning("Unhandled event received");
if (kDebugMode) _logger.warning("Invocation target: ${target.name}, args:\n$args");
return; return;
} }
handler(args); handler(args);

View file

@ -36,7 +36,6 @@ class AuthenticationData {
Map<String, String> get authorizationHeader => { Map<String, String> get authorizationHeader => {
"Authorization": "res $userId:$token", "Authorization": "res $userId:$token",
"UID": uid,
}; };
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {

View file

@ -1,3 +1,5 @@
import 'package:intl/intl.dart';
import 'package:recon/config.dart';
import 'package:recon/crypto_helper.dart'; import 'package:recon/crypto_helper.dart';
import 'package:recon/models/session.dart'; import 'package:recon/models/session.dart';
import 'package:recon/models/session_metadata.dart'; import 'package:recon/models/session_metadata.dart';
@ -10,7 +12,7 @@ enum UserSessionType
graphicalClient, graphicalClient,
chatClient, chatClient,
headless, headless,
not; bot;
factory UserSessionType.fromString(String? text) { factory UserSessionType.fromString(String? text) {
return UserSessionType.values.firstWhere((element) => element.name.toLowerCase() == text?.toLowerCase(), return UserSessionType.values.firstWhere((element) => element.name.toLowerCase() == text?.toLowerCase(),
@ -20,6 +22,7 @@ enum UserSessionType
} }
class UserStatus { class UserStatus {
final String userId;
final OnlineStatus onlineStatus; final OnlineStatus onlineStatus;
final DateTime lastStatusChange; final DateTime lastStatusChange;
final DateTime lastPresenceTimestamp; final DateTime lastPresenceTimestamp;
@ -36,6 +39,7 @@ class UserStatus {
final List<Session> decodedSessions; final List<Session> decodedSessions;
const UserStatus({ const UserStatus({
required this.userId,
required this.onlineStatus, required this.onlineStatus,
required this.lastStatusChange, required this.lastStatusChange,
required this.lastPresenceTimestamp, required this.lastPresenceTimestamp,
@ -54,15 +58,18 @@ class UserStatus {
factory UserStatus.initial() => factory UserStatus.initial() =>
UserStatus.empty().copyWith( UserStatus.empty().copyWith(
compatibilityHash: Config.latestCompatHash,
onlineStatus: OnlineStatus.online, onlineStatus: OnlineStatus.online,
hashSalt: CryptoHelper.cryptoToken(), hashSalt: CryptoHelper.cryptoToken(),
outputDevice: "Screen", outputDevice: "Unknown",
userSessionId: const Uuid().v4().toString(), userSessionId: const Uuid().v4().toString(),
sessionType: UserSessionType.chatClient, sessionType: UserSessionType.chatClient,
isPresent: true,
); );
factory UserStatus.empty() => factory UserStatus.empty() =>
UserStatus( UserStatus(
userId: "",
onlineStatus: OnlineStatus.offline, onlineStatus: OnlineStatus.offline,
lastStatusChange: DateTime.now(), lastStatusChange: DateTime.now(),
lastPresenceTimestamp: DateTime.now(), lastPresenceTimestamp: DateTime.now(),
@ -82,6 +89,7 @@ class UserStatus {
final statusString = map["onlineStatus"].toString(); final statusString = map["onlineStatus"].toString();
final status = OnlineStatus.fromString(statusString); final status = OnlineStatus.fromString(statusString);
return UserStatus( return UserStatus(
userId: map["userId"] ?? "",
onlineStatus: status, onlineStatus: status,
lastStatusChange: DateTime.tryParse(map["lastStatusChange"] ?? "") ?? DateTime.now(), lastStatusChange: DateTime.tryParse(map["lastStatusChange"] ?? "") ?? DateTime.now(),
lastPresenceTimestamp: DateTime.tryParse(map["lastPresenceTimestamp"] ?? "") ?? DateTime.now(), lastPresenceTimestamp: DateTime.tryParse(map["lastPresenceTimestamp"] ?? "") ?? DateTime.now(),
@ -92,7 +100,7 @@ class UserStatus {
appVersion: map["appVersion"] ?? "", appVersion: map["appVersion"] ?? "",
outputDevice: map["outputDevice"] ?? "Unknown", outputDevice: map["outputDevice"] ?? "Unknown",
isMobile: map["isMobile"] ?? false, isMobile: map["isMobile"] ?? false,
compatibilityHash: map["compatabilityHash"] ?? "", compatibilityHash: map["compatibilityHash"] ?? "",
hashSalt: map["hashSalt"] ?? "", hashSalt: map["hashSalt"] ?? "",
sessionType: UserSessionType.fromString(map["sessionType"]) sessionType: UserSessionType.fromString(map["sessionType"])
); );
@ -100,6 +108,7 @@ class UserStatus {
Map toMap({bool shallow = false}) { Map toMap({bool shallow = false}) {
return { return {
"userId": userId,
"onlineStatus": onlineStatus.index, "onlineStatus": onlineStatus.index,
"lastStatusChange": lastStatusChange.toIso8601String(), "lastStatusChange": lastStatusChange.toIso8601String(),
"isPresent": isPresent, "isPresent": isPresent,
@ -117,10 +126,12 @@ class UserStatus {
"outputDevice": outputDevice, "outputDevice": outputDevice,
"isMobile": isMobile, "isMobile": isMobile,
"compatibilityHash": compatibilityHash, "compatibilityHash": compatibilityHash,
"sessionType": toBeginningOfSentenceCase(sessionType.name)
}; };
} }
UserStatus copyWith({ UserStatus copyWith({
String? userId,
OnlineStatus? onlineStatus, OnlineStatus? onlineStatus,
DateTime? lastStatusChange, DateTime? lastStatusChange,
DateTime? lastPresenceTimestamp, DateTime? lastPresenceTimestamp,
@ -137,6 +148,7 @@ class UserStatus {
List<Session>? decodedSessions, List<Session>? decodedSessions,
}) => }) =>
UserStatus( UserStatus(
userId: userId ?? this.userId,
onlineStatus: onlineStatus ?? this.onlineStatus, onlineStatus: onlineStatus ?? this.onlineStatus,
lastStatusChange: lastStatusChange ?? this.lastStatusChange, lastStatusChange: lastStatusChange ?? this.lastStatusChange,
lastPresenceTimestamp: lastPresenceTimestamp ?? this.lastPresenceTimestamp, lastPresenceTimestamp: lastPresenceTimestamp ?? this.lastPresenceTimestamp,

View file

@ -1,3 +1,4 @@
import 'package:collection/collection.dart';
import 'package:recon/client_holder.dart'; import 'package:recon/client_holder.dart';
import 'package:recon/clients/messaging_client.dart'; import 'package:recon/clients/messaging_client.dart';
import 'package:recon/models/users/online_status.dart'; import 'package:recon/models/users/online_status.dart';
@ -52,7 +53,7 @@ class _FriendsListAppBarState extends State<FriendsListAppBar> with AutomaticKee
} }
}, },
itemBuilder: (BuildContext context) => OnlineStatus.values itemBuilder: (BuildContext context) => OnlineStatus.values
.where((element) => element == OnlineStatus.online || element == OnlineStatus.invisible) .where((element) => element == OnlineStatus.online || element == OnlineStatus.offline).sorted((a, b) => b.index.compareTo(a.index),)
.map( .map(
(item) => PopupMenuItem<OnlineStatus>( (item) => PopupMenuItem<OnlineStatus>(
value: item, value: item,

View file

@ -5,7 +5,6 @@ import 'package:recon/auxiliary.dart';
import 'package:recon/clients/inventory_client.dart'; import 'package:recon/clients/inventory_client.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path/path.dart'; import 'package:path/path.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View file

@ -926,7 +926,7 @@ packages:
source: hosted source: hosted
version: "3.0.7" version: "3.0.7"
vector_math: vector_math:
dependency: "direct main" dependency: transitive
description: description:
name: vector_math name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"

View file

@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 0.9.0+1 version: 0.9.1+1
environment: environment:
sdk: '>=3.0.1' sdk: '>=3.0.1'
@ -63,7 +63,6 @@ dependencies:
permission_handler: ^10.2.0 permission_handler: ^10.2.0
flutter_downloader: ^1.10.4 flutter_downloader: ^1.10.4
flutter_cube: ^0.1.1 flutter_cube: ^0.1.1
vector_math: ^2.1.4
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: