Fix notification toggle and add check interval selector
This commit is contained in:
parent
22deb64bce
commit
28fe3fc3c0
9 changed files with 77 additions and 31 deletions
|
@ -22,6 +22,7 @@ class ApiClient {
|
|||
|
||||
final AuthenticationData _authenticationData;
|
||||
|
||||
AuthenticationData get authenticationData => _authenticationData;
|
||||
String get userId => _authenticationData.userId;
|
||||
bool get isAuthenticated => _authenticationData.isAuthenticated;
|
||||
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:contacts_plus_plus/apis/message_api.dart';
|
||||
import 'package:contacts_plus_plus/models/authentication_data.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||
import 'package:contacts_plus_plus/config.dart';
|
||||
import 'package:contacts_plus_plus/models/message.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:workmanager/workmanager.dart';
|
||||
|
||||
enum EventType {
|
||||
unknown,
|
||||
|
@ -31,10 +33,12 @@ class NeosHub {
|
|||
static const String eofChar = "";
|
||||
static const String _negotiationPacket = "{\"protocol\":\"json\", \"version\":1}$eofChar";
|
||||
static const List<int> _reconnectTimeoutsSeconds = [0, 5, 10, 20, 60];
|
||||
static const String taskName = "periodic-unread-check";
|
||||
final ApiClient _apiClient;
|
||||
final Map<String, MessageCache> _messageCache = {};
|
||||
final Map<String, Function> _updateListeners = {};
|
||||
final Logger _logger = Logger("NeosHub");
|
||||
final Workmanager _workmanager = Workmanager();
|
||||
WebSocket? _wsChannel;
|
||||
bool _isConnecting = false;
|
||||
|
||||
|
@ -58,13 +62,27 @@ class NeosHub {
|
|||
return cache;
|
||||
}
|
||||
|
||||
Future<void> checkUnreads() async {
|
||||
final unreads = await MessageApi.getUserMessages(_apiClient, unreadOnly: true);
|
||||
static Future<void> backgroundCheckUnreads(Map<String, dynamic>? inputData) async {
|
||||
if (inputData == null) return;
|
||||
final auth = AuthenticationData.fromMap(inputData);
|
||||
final unreads = await MessageApi.getUserMessages(ApiClient(authenticationData: auth), unreadOnly: true);
|
||||
for (var message in unreads) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _updateNotificationTask(int minuteInterval) async {
|
||||
final auth = _apiClient.authenticationData;
|
||||
if (!auth.isAuthenticated) throw "Unauthenticated";
|
||||
await _workmanager.cancelByUniqueName(taskName);
|
||||
_workmanager.registerPeriodicTask(
|
||||
taskName,
|
||||
taskName,
|
||||
frequency: Duration(minutes: minuteInterval),
|
||||
inputData: auth.toMap(),
|
||||
);
|
||||
}
|
||||
|
||||
void _onDisconnected(error) {
|
||||
_logger.warning("Neos Hub connection died with error '$error', reconnecting...");
|
||||
start();
|
||||
|
|
|
@ -18,6 +18,7 @@ class SettingsClient {
|
|||
_currentSettings = Settings.fromMap(jsonDecode(data));
|
||||
} catch (_) {
|
||||
_storage.delete(key: _settingsKey);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:contacts_plus_plus/clients/neos_hub.dart';
|
||||
import 'package:contacts_plus_plus/clients/settings_client.dart';
|
||||
import 'package:contacts_plus_plus/widgets/friends_list.dart';
|
||||
import 'package:contacts_plus_plus/widgets/login_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:flutter_phoenix/flutter_phoenix.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:workmanager/workmanager.dart';
|
||||
import 'clients/api_client.dart';
|
||||
import 'models/authentication_data.dart';
|
||||
|
||||
void main() async {
|
||||
await Workmanager().initialize(
|
||||
callbackDispatcher, // The top level function, aka callbackDispatcher
|
||||
isInDebugMode: true // If enabled it will post a notification whenever the task is running. Handy for debugging tasks
|
||||
);
|
||||
Logger.root.onRecord.listen((event) => log(event.message, name: event.loggerName));
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
final settingsClient = SettingsClient();
|
||||
|
@ -18,6 +23,17 @@ void main() async {
|
|||
runApp(Phoenix(child: ContactsPlusPlus(settingsClient: settingsClient,)));
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point') // Mandatory if the App is obfuscated or using Flutter 3.1+
|
||||
void callbackDispatcher() {
|
||||
Workmanager().executeTask((String task, Map<String, dynamic>? inputData) async {
|
||||
debugPrint("Native called background task: $task"); //simpleTask will be emitted here.
|
||||
if (task == NeosHub.taskName) {
|
||||
final unreads = NeosHub.backgroundCheckUnreads(inputData);
|
||||
}
|
||||
return Future.value(true);
|
||||
});
|
||||
}
|
||||
|
||||
class ContactsPlusPlus extends StatefulWidget {
|
||||
const ContactsPlusPlus({required this.settingsClient, super.key});
|
||||
|
||||
|
|
|
@ -24,4 +24,12 @@ class AuthenticationData {
|
|||
Map<String, String> get authorizationHeader => {
|
||||
"Authorization": "neos $userId:$token"
|
||||
};
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"userId": userId,
|
||||
"token": token,
|
||||
"secretMachineId": secretMachineId,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:convert';
|
||||
|
||||
class SettingsEntry<T> {
|
||||
final T? value;
|
||||
final T deflt;
|
||||
|
@ -6,7 +8,7 @@ class SettingsEntry<T> {
|
|||
|
||||
factory SettingsEntry.fromMap(Map map) {
|
||||
return SettingsEntry<T>(
|
||||
value: map["value"] as T,
|
||||
value: jsonDecode(map["value"]) as T?,
|
||||
deflt: map["default"],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import 'package:contacts_plus_plus/widgets/expanding_input_fab.dart';
|
|||
import 'package:contacts_plus_plus/widgets/friend_list_tile.dart';
|
||||
import 'package:contacts_plus_plus/widgets/settings_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart' as fln;
|
||||
|
||||
class FriendsList extends StatefulWidget {
|
||||
const FriendsList({super.key});
|
||||
|
|
|
@ -104,7 +104,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||
final requestResult = await notificationManager.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin>()
|
||||
?.requestPermission();
|
||||
await settingsClient.changeSettings(settingsClient.currentSettings.copyWith(notificationsDenied: requestResult));
|
||||
await settingsClient.changeSettings(settingsClient.currentSettings.copyWith(notificationsDenied: requestResult == null ? null : !requestResult));
|
||||
},
|
||||
child: const Text("Yes"),
|
||||
)
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:workmanager/workmanager.dart';
|
||||
|
||||
class SettingsPage extends StatelessWidget {
|
||||
const SettingsPage({super.key});
|
||||
|
||||
static const Map<int, String> _intervalSelections = {
|
||||
5: "5 Minutes",
|
||||
15: "15 Minutes",
|
||||
30: "30 Minutes",
|
||||
60: "1 Hour",
|
||||
120: "2 Hours",
|
||||
300: "6 Hours",
|
||||
600: "12 Hours",
|
||||
};
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -23,33 +32,25 @@ class SettingsPage extends StatelessWidget {
|
|||
children: [
|
||||
const ListSectionHeader(name: "Notifications"),
|
||||
BooleanSettingsTile(
|
||||
title: "Send Notifications",
|
||||
initialState: sClient.currentSettings.notificationsDenied.valueOrDefault,
|
||||
onChanged: (value) async => await sClient.changeSettings(sClient.currentSettings.copyWith(notificationsDenied: value)),
|
||||
title: "Enable Notifications",
|
||||
initialState: !sClient.currentSettings.notificationsDenied.valueOrDefault,
|
||||
onChanged: (value) async => await sClient.changeSettings(sClient.currentSettings.copyWith(notificationsDenied: !value)),
|
||||
),
|
||||
ListTile(
|
||||
trailing: const Icon(Icons.logout),
|
||||
title: const Text("Sign out"),
|
||||
trailing: StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return DropdownButton<int>(
|
||||
items: _intervalSelections.keys.map((e) => DropdownMenuItem<int>(value: e, child: Text("${_intervalSelections[e]}"))).toList(),
|
||||
value: sClient.currentSettings.unreadCheckIntervalMinutes.valueOrDefault,
|
||||
onChanged: (int? value) async {
|
||||
await sClient.changeSettings(sClient.currentSettings.copyWith(unreadCheckIntervalMinutes: value));
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
}
|
||||
),
|
||||
title: const Text("Check Interval"),
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
AlertDialog(
|
||||
title: Text("Are you sure you want to sign out?", style: Theme
|
||||
.of(context)
|
||||
.textTheme
|
||||
.titleLarge,),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text("No")),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await ClientHolder.of(context).apiClient.logout(context);
|
||||
},
|
||||
child: const Text("Yes"),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const ListSectionHeader(name: "Other"),
|
||||
|
|
Loading…
Reference in a new issue