notificationSettings #354

Merged
dan merged 9 commits from notificationSettings into trunk 2022-02-08 23:21:28 +00:00
23 changed files with 559 additions and 91 deletions

View File

@ -1 +1 @@
2022-02-08-16-19-v1.5.4-36-g4467c40 2022-02-08-22-37-v1.5.4-41-gd0d5300

View File

@ -1 +1 @@
2022-02-08-21-19-v1.5.4-36-g4467c40 2022-02-08-22-37-v1.5.4-41-gd0d5300

View File

@ -29,6 +29,9 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
private var notificationID: MutableMap<String, Int> = mutableMapOf() private var notificationID: MutableMap<String, Int> = mutableMapOf()
private var notificationIDnext: Int = 1 private var notificationIDnext: Int = 1
private var notificationSimple: String? = null
private var notificationConversationInfo: String? = null
override suspend fun doWork(): Result { override suspend fun doWork(): Result {
val method = inputData.getString(KEY_METHOD) val method = inputData.getString(KEY_METHOD)
?: return Result.failure() ?: return Result.failure()
@ -67,36 +70,64 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
val data = JSONObject(evt.Data) val data = JSONObject(evt.Data)
val handle = if (evt.EventType == "NewMessageFromPeer") data.getString("RemotePeer") else data.getString("GroupID"); val handle = if (evt.EventType == "NewMessageFromPeer") data.getString("RemotePeer") else data.getString("GroupID");
if (data["RemotePeer"] != data["ProfileOnion"]) { if (data["RemotePeer"] != data["ProfileOnion"]) {
val channelId = val notification = data["notification"]
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createMessageNotificationChannel(handle, handle) if (notification == "SimpleEvent") {
} else { val channelId =
// If earlier version channel ID is not used if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) createMessageNotificationChannel("Cwtch", "Cwtch")
"" } else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
}
val clickIntent = Intent(applicationContext, MainActivity::class.java).also { intent ->
intent.action = Intent.ACTION_RUN
intent.putExtra("EventType", "NotificationClicked")
} }
val loader = FlutterInjector.instance().flutterLoader() val newNotification = NotificationCompat.Builder(applicationContext, channelId)
val key = loader.getLookupKeyForAsset("assets/" + data.getString("picture"))//"assets/profiles/001-centaur.png") .setContentTitle("Cwtch")
val fh = applicationContext.assets.open(key) .setContentText(notificationSimple ?: "New Message")
.setSmallIcon(R.mipmap.knott_transparent)
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setAutoCancel(true)
.build()
notificationManager.notify(getNotificationID("Cwtch", "Cwtch"), newNotification)
} else if (notification == "ContactInfo") {
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createMessageNotificationChannel(handle, handle)
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
}
val loader = FlutterInjector.instance().flutterLoader()
val key = loader.getLookupKeyForAsset("assets/" + data.getString("Picture"))//"assets/profiles/001-centaur.png")
val fh = applicationContext.assets.open(key)
val clickIntent = Intent(applicationContext, MainActivity::class.java).also { intent -> val clickIntent = Intent(applicationContext, MainActivity::class.java).also { intent ->
intent.action = Intent.ACTION_RUN intent.action = Intent.ACTION_RUN
intent.putExtra("EventType", "NotificationClicked") intent.putExtra("EventType", "NotificationClicked")
intent.putExtra("ProfileOnion", data.getString("ProfileOnion")) intent.putExtra("ProfileOnion", data.getString("ProfileOnion"))
intent.putExtra("Handle", handle) intent.putExtra("Handle", handle)
} }
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
.setContentTitle(data.getString("Nick"))
.setContentText((notificationConversationInfo ?: "New Message From %1").replace("%1", data.getString("Nick")))
.setLargeIcon(BitmapFactory.decodeStream(fh))
.setSmallIcon(R.mipmap.knott_transparent)
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setAutoCancel(true)
.build()
notificationManager.notify(getNotificationID(data.getString("ProfileOnion"), handle), newNotification)
}
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
.setContentTitle(data.getString("Nick"))
.setContentText("New message")//todo: translate
.setLargeIcon(BitmapFactory.decodeStream(fh))
.setSmallIcon(R.mipmap.knott_transparent)
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setAutoCancel(true)
.build()
notificationManager.notify(getNotificationID(data.getString("ProfileOnion"), handle), newNotification)
} }
} else if (evt.EventType == "FileDownloadProgressUpdate") { } else if (evt.EventType == "FileDownloadProgressUpdate") {
try { try {
@ -363,6 +394,11 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
val v = (a.get("Val") as? String) ?: "" val v = (a.get("Val") as? String) ?: ""
Cwtch.setServerAttribute(serverOnion, key, v) Cwtch.setServerAttribute(serverOnion, key, v)
} }
"L10nInit" -> {
notificationSimple = (a.get("notificationSimple") as? String) ?: "New Message"
notificationConversationInfo = (a.get("notificationConversationInfo") as? String)
?: "New Message From "
}
else -> { else -> {
Log.i("FlwtchWorker", "unknown command: " + method); Log.i("FlwtchWorker", "unknown command: " + method);
return Result.failure() return Result.failure()

View File

@ -105,5 +105,9 @@ abstract class Cwtch {
// non-ffi // non-ffi
String defaultDownloadPath(); String defaultDownloadPath();
bool isL10nInit();
void l10nInit(String notificationSimple, String notificationConversationInfo);
void dispose(); void dispose();
} }

View File

@ -9,6 +9,7 @@ import 'package:cwtch/models/remoteserver.dart';
import 'package:cwtch/models/servers.dart'; import 'package:cwtch/models/servers.dart';
import 'package:cwtch/notification_manager.dart'; import 'package:cwtch/notification_manager.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:cwtch/torstatus.dart'; import 'package:cwtch/torstatus.dart';
@ -28,6 +29,9 @@ class CwtchNotifier {
late AppState appState; late AppState appState;
late ServerListState serverListState; late ServerListState serverListState;
String? notificationSimple;
String? notificationConversationInfo;
CwtchNotifier( CwtchNotifier(
ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN, TorStatus torStatusCN, NotificationsManager notificationManagerP, AppState appStateCN, ServerListState serverListStateCN) { ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN, TorStatus torStatusCN, NotificationsManager notificationManagerP, AppState appStateCN, ServerListState serverListStateCN) {
profileCN = pcn; profileCN = pcn;
@ -39,6 +43,11 @@ class CwtchNotifier {
serverListState = serverListStateCN; serverListState = serverListStateCN;
} }
void l10nInit(String notificationSimple, String notificationConversationInfo) {
this.notificationSimple = notificationSimple;
this.notificationConversationInfo = notificationConversationInfo;
}
void handleMessage(String type, dynamic data) { void handleMessage(String type, dynamic data) {
//EnvironmentConfig.debugLog("NewEvent $type $data"); //EnvironmentConfig.debugLog("NewEvent $type $data");
switch (type) { switch (type) {
@ -60,24 +69,22 @@ class CwtchNotifier {
case "ContactCreated": case "ContactCreated":
EnvironmentConfig.debugLog("ContactCreated $data"); EnvironmentConfig.debugLog("ContactCreated $data");
profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState( profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(data["ProfileOnion"], int.parse(data["ConversationID"]), data["RemotePeer"],
data["ProfileOnion"], nickname: data["nick"],
int.parse(data["ConversationID"]), status: data["status"],
data["RemotePeer"], imagePath: data["picture"],
nickname: data["nick"], defaultImagePath: data["defaultPicture"],
status: data["status"], blocked: data["blocked"] == "true",
imagePath: data["picture"], accepted: data["accepted"] == "true",
defaultImagePath: data["defaultPicture"], savePeerHistory: data["saveConversationHistory"] == null ? "DeleteHistoryConfirmed" : data["saveConversationHistory"],
blocked: data["blocked"] == "true", numMessages: int.parse(data["numMessages"]),
accepted: data["accepted"] == "true", numUnread: int.parse(data["unread"]),
savePeerHistory: data["saveConversationHistory"] == null ? "DeleteHistoryConfirmed" : data["saveConversationHistory"], isGroup: false, // by definition
numMessages: int.parse(data["numMessages"]), server: null,
Review

wrong line

wrong line
numUnread: int.parse(data["unread"]), archived: false,
isGroup: false, // by definition lastMessageTime: DateTime.now(), //show at the top of the contact list even if no messages yet
server: null, notificationPolicy: data["notificationPolicy"] ?? "ConversationNotificationPolicy.Default"));
archived: false,
Review

wrong line

wrong line
lastMessageTime: DateTime.now(), //show at the top of the contact list even if no messages yet
));
break; break;
case "NewServer": case "NewServer":
EnvironmentConfig.debugLog("NewServer $data"); EnvironmentConfig.debugLog("NewServer $data");
@ -113,7 +120,9 @@ class CwtchNotifier {
status: status, status: status,
server: data["GroupServer"], server: data["GroupServer"],
isGroup: true, isGroup: true,
lastMessageTime: DateTime.now())); lastMessageTime: DateTime.now(),
notificationPolicy: data["notificationPolicy"] ?? "ConversationNotificationPolicy.Default"));
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(int.parse(data["ConversationID"]), DateTime.now()); profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(int.parse(data["ConversationID"]), DateTime.now());
} }
break; break;
@ -144,7 +153,6 @@ class CwtchNotifier {
} }
break; break;
case "NewMessageFromPeer": case "NewMessageFromPeer":
notificationManager.notify("New Message From Peer!");
var identifier = int.parse(data["ConversationID"]); var identifier = int.parse(data["ConversationID"]);
var messageID = int.parse(data["Index"]); var messageID = int.parse(data["Index"]);
var timestamp = DateTime.tryParse(data['TimestampReceived'])!; var timestamp = DateTime.tryParse(data['TimestampReceived'])!;
@ -154,6 +162,14 @@ class CwtchNotifier {
String? contenthash = data['ContentHash']; String? contenthash = data['ContentHash'];
var selectedProfile = appState.selectedProfile == data["ProfileOnion"]; var selectedProfile = appState.selectedProfile == data["ProfileOnion"];
var selectedConversation = selectedProfile && appState.selectedConversation == identifier; var selectedConversation = selectedProfile && appState.selectedConversation == identifier;
var notification = data["notification"];
if (notification == "SimpleEvent") {
notificationManager.notify(notificationSimple ?? "New Message");
} else if (notification == "ContactInfo") {
var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier);
notificationManager.notify((notificationConversationInfo ?? "New Message from %1").replaceFirst("%1", (contact?.nickname ?? senderHandle.toString())));
}
profileCN.getProfile(data["ProfileOnion"])?.newMessage( profileCN.getProfile(data["ProfileOnion"])?.newMessage(
identifier, identifier,
@ -209,6 +225,7 @@ class CwtchNotifier {
String? contenthash = data['ContentHash']; String? contenthash = data['ContentHash'];
var selectedProfile = appState.selectedProfile == data["ProfileOnion"]; var selectedProfile = appState.selectedProfile == data["ProfileOnion"];
var selectedConversation = selectedProfile && appState.selectedConversation == identifier; var selectedConversation = selectedProfile && appState.selectedConversation == identifier;
var notification = data["notification"];
// Only bother to do anything if we know about the group and the provided index is greater than our current total... // Only bother to do anything if we know about the group and the provided index is greater than our current total...
if (currentTotal != null && idx >= currentTotal) { if (currentTotal != null && idx >= currentTotal) {
@ -224,7 +241,12 @@ class CwtchNotifier {
// and `local now`. // and `local now`.
profileCN.getProfile(data["ProfileOnion"])?.newMessage(identifier, idx, timestampSent, senderHandle, senderImage, isAuto, data["Data"], contenthash, selectedProfile, selectedConversation); profileCN.getProfile(data["ProfileOnion"])?.newMessage(identifier, idx, timestampSent, senderHandle, senderImage, isAuto, data["Data"], contenthash, selectedProfile, selectedConversation);
notificationManager.notify("New Message From Group!"); if (notification == "SimpleEvent") {
notificationManager.notify(notificationSimple ?? "New Message");
} else if (notification == "ContactInfo") {
var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier);
notificationManager.notify((notificationConversationInfo ?? "New Message from %1").replaceFirst("%1", (contact?.nickname ?? senderHandle.toString())));
}
appState.notifyProfileUnread(); appState.notifyProfileUnread();
} }
RemoteServerInfoState? server = profileCN.getProfile(data["ProfileOnion"])?.serverList.getServer(contact.server ?? ""); RemoteServerInfoState? server = profileCN.getProfile(data["ProfileOnion"])?.serverList.getServer(contact.server ?? "");

View File

@ -100,6 +100,7 @@ class CwtchFfi implements Cwtch {
late CwtchNotifier cwtchNotifier; late CwtchNotifier cwtchNotifier;
late Isolate cwtchIsolate; late Isolate cwtchIsolate;
ReceivePort _receivePort = ReceivePort(); ReceivePort _receivePort = ReceivePort();
bool _isL10nInit = false;
static String getLibraryPath() { static String getLibraryPath() {
if (Platform.isWindows) { if (Platform.isWindows) {
@ -744,4 +745,15 @@ class CwtchFfi implements Cwtch {
malloc.free(utf8newpass); malloc.free(utf8newpass);
malloc.free(utf8newpasssagain); malloc.free(utf8newpasssagain);
} }
@override
bool isL10nInit() {
return _isL10nInit;
}
@override
void l10nInit(String notificationSimple, String notificationConversationInfo) {
cwtchNotifier.l10nInit(notificationSimple, notificationConversationInfo);
_isL10nInit = true;
}
} }

View File

@ -30,6 +30,7 @@ class CwtchGomobile implements Cwtch {
late Future<dynamic> androidHomeDirectory; late Future<dynamic> androidHomeDirectory;
String androidHomeDirectoryStr = ""; String androidHomeDirectoryStr = "";
late CwtchNotifier cwtchNotifier; late CwtchNotifier cwtchNotifier;
bool _isL10nInit = false;
CwtchGomobile(CwtchNotifier _cwtchNotifier) { CwtchGomobile(CwtchNotifier _cwtchNotifier) {
print("gomobile.dart: CwtchGomobile()"); print("gomobile.dart: CwtchGomobile()");
@ -295,4 +296,16 @@ class CwtchGomobile implements Cwtch {
void ChangePassword(String profile, String pass, String newpass, String newpassAgain) { void ChangePassword(String profile, String pass, String newpass, String newpassAgain) {
cwtchPlatform.invokeMethod("ChangePassword", {"ProfileOnion": profile, "OldPass": pass, "NewPass": newpass, "NewPassAgain": newpassAgain}); cwtchPlatform.invokeMethod("ChangePassword", {"ProfileOnion": profile, "OldPass": pass, "NewPass": newpass, "NewPassAgain": newpassAgain});
} }
@override
bool isL10nInit() {
return _isL10nInit;
}
@override
void l10nInit(String notificationSimple, String notificationConversationInfo) {
cwtchNotifier.l10nInit(notificationSimple, notificationConversationInfo);
cwtchPlatform.invokeMethod("L10nInit", {"notificationSimple": notificationSimple, "notificationConversationInfo": notificationConversationInfo});
_isL10nInit = true;
}
} }

View File

@ -1,6 +1,25 @@
{ {
"@@locale": "de", "@@locale": "de",
"@@last_modified": "2022-02-07T21:17:01+01:00", "@@last_modified": "2022-02-08T20:13:50+01:00",
"newMessageNotificationConversationInfo": "New Message From %1",
"newMessageNotificationSimple": "New Message",
"notificationContentContactInfo": "Conversation Information",
"notificationContentSimpleEvent": "Plain Event",
"conversationNotificationPolicySettingDescription": "Control this conversation's notification behaviour",
"conversationNotificationPolicySettingLabel": "Conversation Notification Policy",
"settingsGroupExperiments": "Experiments",
"settingsGroupAppearance": "Appearance",
"settingGroupBehaviour": "Behaviour",
"notificationContentSettingDescription": "Controls the contents of conversation notifications",
"notificationPolicySettingDescription": "Controls the default application notification behaviour",
"notificationContentSettingLabel": "Notification Content",
"notificationPolicySettingLabel": "Notification Policy",
"conversationNotificationPolicyNever": "Never",
"conversationNotificationPolicyOptIn": "Opt In",
"conversationNotificationPolicyDefault": "Default",
"notificationPolicyDefaultAll": "Default All",
"notificationPolicyOptIn": "Opt In",
"notificationPolicyMute": "Mute",
"tooltipSelectACustomProfileImage": "Select a Custom Profile Image", "tooltipSelectACustomProfileImage": "Select a Custom Profile Image",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.", "torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus", "torSettingsEnableCache": "Cache Tor Consensus",

View File

@ -1,6 +1,25 @@
{ {
"@@locale": "en", "@@locale": "en",
"@@last_modified": "2022-02-07T21:17:01+01:00", "@@last_modified": "2022-02-08T20:13:50+01:00",
"newMessageNotificationConversationInfo": "New Message From %1",
"newMessageNotificationSimple": "New Message",
"notificationContentContactInfo": "Conversation Information",
"notificationContentSimpleEvent": "Plain Event",
"conversationNotificationPolicySettingDescription": "Control this conversation's notification behaviour",
"conversationNotificationPolicySettingLabel": "Conversation Notification Policy",
"settingsGroupExperiments": "Experiments",
"settingsGroupAppearance": "Appearance",
"settingGroupBehaviour": "Behaviour",
"notificationContentSettingDescription": "Controls the contents of conversation notifications",
"notificationPolicySettingDescription": "Controls the default application notification behaviour",
"notificationContentSettingLabel": "Notification Content",
"notificationPolicySettingLabel": "Notification Policy",
"conversationNotificationPolicyNever": "Never",
"conversationNotificationPolicyOptIn": "Opt In",
"conversationNotificationPolicyDefault": "Default",
"notificationPolicyDefaultAll": "Default All",
"notificationPolicyOptIn": "Opt In",
"notificationPolicyMute": "Mute",
"tooltipSelectACustomProfileImage": "Select a Custom Profile Image", "tooltipSelectACustomProfileImage": "Select a Custom Profile Image",
"editProfile": "Edit Profile", "editProfile": "Edit Profile",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.", "torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",

View File

@ -1,6 +1,25 @@
{ {
"@@locale": "es", "@@locale": "es",
"@@last_modified": "2022-02-07T21:17:01+01:00", "@@last_modified": "2022-02-08T20:13:50+01:00",
"newMessageNotificationConversationInfo": "New Message From %1",
"newMessageNotificationSimple": "New Message",
"notificationContentContactInfo": "Conversation Information",
"notificationContentSimpleEvent": "Plain Event",
"conversationNotificationPolicySettingDescription": "Control this conversation's notification behaviour",
"conversationNotificationPolicySettingLabel": "Conversation Notification Policy",
"settingsGroupExperiments": "Experiments",
"settingsGroupAppearance": "Appearance",
"settingGroupBehaviour": "Behaviour",
"notificationContentSettingDescription": "Controls the contents of conversation notifications",
"notificationPolicySettingDescription": "Controls the default application notification behaviour",
"notificationContentSettingLabel": "Notification Content",
"notificationPolicySettingLabel": "Notification Policy",
"conversationNotificationPolicyNever": "Never",
"conversationNotificationPolicyOptIn": "Opt In",
"conversationNotificationPolicyDefault": "Default",
"notificationPolicyDefaultAll": "Default All",
"notificationPolicyOptIn": "Opt In",
"notificationPolicyMute": "Mute",
"tooltipSelectACustomProfileImage": "Select a Custom Profile Image", "tooltipSelectACustomProfileImage": "Select a Custom Profile Image",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.", "torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus", "torSettingsEnableCache": "Cache Tor Consensus",

View File

@ -1,6 +1,25 @@
{ {
"@@locale": "fr", "@@locale": "fr",
"@@last_modified": "2022-02-07T21:17:01+01:00", "@@last_modified": "2022-02-08T20:13:50+01:00",
"newMessageNotificationConversationInfo": "New Message From %1",
"newMessageNotificationSimple": "New Message",
"notificationContentContactInfo": "Conversation Information",
"notificationContentSimpleEvent": "Plain Event",
"conversationNotificationPolicySettingDescription": "Control this conversation's notification behaviour",
"conversationNotificationPolicySettingLabel": "Conversation Notification Policy",
"settingsGroupExperiments": "Experiments",
"settingsGroupAppearance": "Appearance",
"settingGroupBehaviour": "Behaviour",
"notificationContentSettingDescription": "Controls the contents of conversation notifications",
"notificationPolicySettingDescription": "Controls the default application notification behaviour",
"notificationContentSettingLabel": "Notification Content",
"notificationPolicySettingLabel": "Notification Policy",
"conversationNotificationPolicyNever": "Never",
"conversationNotificationPolicyOptIn": "Opt In",
"conversationNotificationPolicyDefault": "Default",
"notificationPolicyDefaultAll": "Default All",
"notificationPolicyOptIn": "Opt In",
"notificationPolicyMute": "Mute",
"tooltipSelectACustomProfileImage": "Select a Custom Profile Image", "tooltipSelectACustomProfileImage": "Select a Custom Profile Image",
"editProfile": "Modifier le profil", "editProfile": "Modifier le profil",
"settingTheme": "Utilisez des thèmes clairs", "settingTheme": "Utilisez des thèmes clairs",

View File

@ -1,6 +1,25 @@
{ {
"@@locale": "it", "@@locale": "it",
"@@last_modified": "2022-02-07T21:17:01+01:00", "@@last_modified": "2022-02-08T20:13:50+01:00",
"newMessageNotificationConversationInfo": "New Message From %1",
"newMessageNotificationSimple": "New Message",
"notificationContentContactInfo": "Conversation Information",
"notificationContentSimpleEvent": "Plain Event",
"conversationNotificationPolicySettingDescription": "Control this conversation's notification behaviour",
"conversationNotificationPolicySettingLabel": "Conversation Notification Policy",
"settingsGroupExperiments": "Experiments",
"settingsGroupAppearance": "Appearance",
"settingGroupBehaviour": "Behaviour",
"notificationContentSettingDescription": "Controls the contents of conversation notifications",
"notificationPolicySettingDescription": "Controls the default application notification behaviour",
"notificationContentSettingLabel": "Notification Content",
"notificationPolicySettingLabel": "Notification Policy",
"conversationNotificationPolicyNever": "Never",
"conversationNotificationPolicyOptIn": "Opt In",
"conversationNotificationPolicyDefault": "Default",
"notificationPolicyDefaultAll": "Default All",
"notificationPolicyOptIn": "Opt In",
"notificationPolicyMute": "Mute",
"tooltipSelectACustomProfileImage": "Select a Custom Profile Image", "tooltipSelectACustomProfileImage": "Select a Custom Profile Image",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.", "torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus", "torSettingsEnableCache": "Cache Tor Consensus",

View File

@ -1,6 +1,25 @@
{ {
"@@locale": "pl", "@@locale": "pl",
"@@last_modified": "2022-02-07T21:17:01+01:00", "@@last_modified": "2022-02-08T20:13:50+01:00",
"newMessageNotificationConversationInfo": "New Message From %1",
"newMessageNotificationSimple": "New Message",
"notificationContentContactInfo": "Conversation Information",
"notificationContentSimpleEvent": "Plain Event",
"conversationNotificationPolicySettingDescription": "Control this conversation's notification behaviour",
"conversationNotificationPolicySettingLabel": "Conversation Notification Policy",
"settingsGroupExperiments": "Experiments",
"settingsGroupAppearance": "Appearance",
"settingGroupBehaviour": "Behaviour",
"notificationContentSettingDescription": "Controls the contents of conversation notifications",
"notificationPolicySettingDescription": "Controls the default application notification behaviour",
"notificationContentSettingLabel": "Notification Content",
"notificationPolicySettingLabel": "Notification Policy",
"conversationNotificationPolicyNever": "Never",
"conversationNotificationPolicyOptIn": "Opt In",
"conversationNotificationPolicyDefault": "Default",
"notificationPolicyDefaultAll": "Default All",
"notificationPolicyOptIn": "Opt In",
"notificationPolicyMute": "Mute",
"tooltipSelectACustomProfileImage": "Select a Custom Profile Image", "tooltipSelectACustomProfileImage": "Select a Custom Profile Image",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.", "torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus", "torSettingsEnableCache": "Cache Tor Consensus",

View File

@ -1,6 +1,25 @@
{ {
"@@locale": "pt", "@@locale": "pt",
"@@last_modified": "2022-02-07T21:17:01+01:00", "@@last_modified": "2022-02-08T20:13:50+01:00",
"newMessageNotificationConversationInfo": "New Message From %1",
"newMessageNotificationSimple": "New Message",
"notificationContentContactInfo": "Conversation Information",
"notificationContentSimpleEvent": "Plain Event",
"conversationNotificationPolicySettingDescription": "Control this conversation's notification behaviour",
"conversationNotificationPolicySettingLabel": "Conversation Notification Policy",
"settingsGroupExperiments": "Experiments",
"settingsGroupAppearance": "Appearance",
"settingGroupBehaviour": "Behaviour",
"notificationContentSettingDescription": "Controls the contents of conversation notifications",
"notificationPolicySettingDescription": "Controls the default application notification behaviour",
"notificationContentSettingLabel": "Notification Content",
"notificationPolicySettingLabel": "Notification Policy",
"conversationNotificationPolicyNever": "Never",
"conversationNotificationPolicyOptIn": "Opt In",
"conversationNotificationPolicyDefault": "Default",
"notificationPolicyDefaultAll": "Default All",
"notificationPolicyOptIn": "Opt In",
"notificationPolicyMute": "Mute",
"tooltipSelectACustomProfileImage": "Select a Custom Profile Image", "tooltipSelectACustomProfileImage": "Select a Custom Profile Image",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.", "torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus", "torSettingsEnableCache": "Cache Tor Consensus",

View File

@ -1,6 +1,25 @@
{ {
"@@locale": "ru", "@@locale": "ru",
"@@last_modified": "2022-02-07T21:17:01+01:00", "@@last_modified": "2022-02-08T20:13:50+01:00",
"newMessageNotificationConversationInfo": "New Message From %1",
"newMessageNotificationSimple": "New Message",
"notificationContentContactInfo": "Conversation Information",
"notificationContentSimpleEvent": "Plain Event",
"conversationNotificationPolicySettingDescription": "Control this conversation's notification behaviour",
"conversationNotificationPolicySettingLabel": "Conversation Notification Policy",
"settingsGroupExperiments": "Experiments",
"settingsGroupAppearance": "Appearance",
"settingGroupBehaviour": "Behaviour",
"notificationContentSettingDescription": "Controls the contents of conversation notifications",
"notificationPolicySettingDescription": "Controls the default application notification behaviour",
"notificationContentSettingLabel": "Notification Content",
"notificationPolicySettingLabel": "Notification Policy",
"conversationNotificationPolicyNever": "Never",
"conversationNotificationPolicyOptIn": "Opt In",
"conversationNotificationPolicyDefault": "Default",
"notificationPolicyDefaultAll": "Default All",
"notificationPolicyOptIn": "Opt In",
"notificationPolicyMute": "Mute",
"tooltipSelectACustomProfileImage": "Select a Custom Profile Image", "tooltipSelectACustomProfileImage": "Select a Custom Profile Image",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.", "torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus", "torSettingsEnableCache": "Cache Tor Consensus",

View File

@ -37,7 +37,6 @@ Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
print("runApp()"); print("runApp()");
runApp(Flwtch()); runApp(Flwtch());
sleep(Duration(seconds: 1));
} }
class Flwtch extends StatefulWidget { class Flwtch extends StatefulWidget {

View File

@ -1,15 +1,38 @@
import 'package:cwtch/widgets/messagerow.dart'; import 'package:cwtch/widgets/messagerow.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'message.dart'; import 'message.dart';
import 'messagecache.dart'; import 'messagecache.dart';
enum ConversationNotificationPolicy {
Default,
OptIn,
Never,
}
extension Nameable on ConversationNotificationPolicy {
String toName(BuildContext context) {
switch (this) {
case ConversationNotificationPolicy.Default:
return AppLocalizations.of(context)!.conversationNotificationPolicyDefault;
case ConversationNotificationPolicy.OptIn:
return AppLocalizations.of(context)!.conversationNotificationPolicyOptIn;
case ConversationNotificationPolicy.Never:
return AppLocalizations.of(context)!.conversationNotificationPolicyNever;
}
}
}
class ContactInfoState extends ChangeNotifier { class ContactInfoState extends ChangeNotifier {
final String profileOnion; final String profileOnion;
final int identifier; final int identifier;
final String onion; final String onion;
late String _nickname; late String _nickname;
late ConversationNotificationPolicy _notificationPolicy;
late bool _accepted; late bool _accepted;
late bool _blocked; late bool _blocked;
late String _status; late String _status;
@ -44,7 +67,8 @@ class ContactInfoState extends ChangeNotifier {
numUnread = 0, numUnread = 0,
lastMessageTime, lastMessageTime,
server, server,
archived = false}) { archived = false,
notificationPolicy = "ConversationNotificationPolicy.Default"}) {
this._nickname = nickname; this._nickname = nickname;
this._isGroup = isGroup; this._isGroup = isGroup;
this._accepted = accepted; this._accepted = accepted;
@ -58,6 +82,7 @@ class ContactInfoState extends ChangeNotifier {
this._lastMessageTime = lastMessageTime == null ? DateTime.fromMillisecondsSinceEpoch(0) : lastMessageTime; this._lastMessageTime = lastMessageTime == null ? DateTime.fromMillisecondsSinceEpoch(0) : lastMessageTime;
this._server = server; this._server = server;
this._archived = archived; this._archived = archived;
this._notificationPolicy = notificationPolicyFromString(notificationPolicy);
this.messageCache = new MessageCache(); this.messageCache = new MessageCache();
keys = Map<String, GlobalKey<MessageRowState>>(); keys = Map<String, GlobalKey<MessageRowState>>();
} }
@ -201,6 +226,13 @@ class ContactInfoState extends ChangeNotifier {
} }
} }
ConversationNotificationPolicy get notificationsPolicy => _notificationPolicy;
set notificationsPolicy(ConversationNotificationPolicy newVal) {
_notificationPolicy = newVal;
notifyListeners();
}
GlobalKey<MessageRowState> getMessageKey(int conversation, int message) { GlobalKey<MessageRowState> getMessageKey(int conversation, int message) {
String index = "c: " + conversation.toString() + " m:" + message.toString(); String index = "c: " + conversation.toString() + " m:" + message.toString();
if (keys[index] == null) { if (keys[index] == null) {
@ -244,4 +276,16 @@ class ContactInfoState extends ChangeNotifier {
this.messageCache.ackCache(messageID); this.messageCache.ackCache(messageID);
notifyListeners(); notifyListeners();
} }
static ConversationNotificationPolicy notificationPolicyFromString(String val) {
switch (val) {
case "ConversationNotificationPolicy.Default":
return ConversationNotificationPolicy.Default;
case "ConversationNotificationPolicy.OptIn":
return ConversationNotificationPolicy.OptIn;
case "ConversationNotificationPolicy.Never":
return ConversationNotificationPolicy.Never;
}
return ConversationNotificationPolicy.Never;
}
} }

View File

@ -63,7 +63,8 @@ class ProfileInfoState extends ChangeNotifier {
isGroup: contact["isGroup"], isGroup: contact["isGroup"],
server: contact["groupServer"], server: contact["groupServer"],
archived: contact["isArchived"] == true, archived: contact["isArchived"] == true,
lastMessageTime: DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"]))); lastMessageTime: DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"])),
notificationPolicy: contact["notificationPolicy"] ?? "ConversationNotificationPolicy.Default");
})); }));
// dummy set to invoke sort-on-load // dummy set to invoke sort-on-load
@ -100,6 +101,7 @@ class ProfileInfoState extends ChangeNotifier {
// Getters and Setters for Online Status // Getters and Setters for Online Status
bool get isOnline => this._online; bool get isOnline => this._online;
set isOnline(bool newValue) { set isOnline(bool newValue) {
this._online = newValue; this._online = newValue;
notifyListeners(); notifyListeners();
@ -109,24 +111,28 @@ class ProfileInfoState extends ChangeNotifier {
bool get isEncrypted => this._encrypted; bool get isEncrypted => this._encrypted;
String get nickname => this._nickname; String get nickname => this._nickname;
set nickname(String newValue) { set nickname(String newValue) {
this._nickname = newValue; this._nickname = newValue;
notifyListeners(); notifyListeners();
} }
String get imagePath => this._imagePath; String get imagePath => this._imagePath;
set imagePath(String newVal) { set imagePath(String newVal) {
this._imagePath = newVal; this._imagePath = newVal;
notifyListeners(); notifyListeners();
} }
String get defaultImagePath => this._defaultImagePath; String get defaultImagePath => this._defaultImagePath;
set defaultImagePath(String newVal) { set defaultImagePath(String newVal) {
this._defaultImagePath = newVal; this._defaultImagePath = newVal;
notifyListeners(); notifyListeners();
} }
int get unreadMessages => this._unreadMessages; int get unreadMessages => this._unreadMessages;
set unreadMessages(int newVal) { set unreadMessages(int newVal) {
this._unreadMessages = newVal; this._unreadMessages = newVal;
notifyListeners(); notifyListeners();
@ -144,6 +150,7 @@ class ProfileInfoState extends ChangeNotifier {
} }
ContactListState get contactList => this._contacts; ContactListState get contactList => this._contacts;
ProfileServerListState get serverList => this._servers; ProfileServerListState get serverList => this._servers;
@override @override
@ -183,6 +190,7 @@ class ProfileInfoState extends ChangeNotifier {
isGroup: contact["isGroup"], isGroup: contact["isGroup"],
server: contact["groupServer"], server: contact["groupServer"],
lastMessageTime: DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"])), lastMessageTime: DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"])),
notificationPolicy: contact["notificationPolicy"] ?? "ConversationNotificationPolicy.Default",
)); ));
} }
unreadMessages += int.parse(contact["numUnread"]); unreadMessages += int.parse(contact["numUnread"]);

View File

@ -22,6 +22,17 @@ enum DualpaneMode {
CopyPortrait, CopyPortrait,
} }
enum NotificationPolicy {
Mute,
OptIn,
DefaultAll,
}
enum NotificationContent {
SimpleEvent,
ContactInfo,
}
/// Settings govern the *Globally* relevant settings like Locale, Theme and Experiments. /// Settings govern the *Globally* relevant settings like Locale, Theme and Experiments.
/// We also provide access to the version information here as it is also accessed from the /// We also provide access to the version information here as it is also accessed from the
/// Settings Pane. /// Settings Pane.
@ -29,12 +40,16 @@ class Settings extends ChangeNotifier {
Locale locale; Locale locale;
late PackageInfo packageInfo; late PackageInfo packageInfo;
OpaqueThemeType theme; OpaqueThemeType theme;
// explicitly set experiments to false until told otherwise... // explicitly set experiments to false until told otherwise...
bool experimentsEnabled = false; bool experimentsEnabled = false;
HashMap<String, bool> experiments = HashMap.identity(); HashMap<String, bool> experiments = HashMap.identity();
DualpaneMode _uiColumnModePortrait = DualpaneMode.Single; DualpaneMode _uiColumnModePortrait = DualpaneMode.Single;
DualpaneMode _uiColumnModeLandscape = DualpaneMode.CopyPortrait; DualpaneMode _uiColumnModeLandscape = DualpaneMode.CopyPortrait;
NotificationPolicy _notificationPolicy = NotificationPolicy.DefaultAll;
NotificationContent _notificationContent = NotificationContent.SimpleEvent;
bool blockUnknownConnections = false; bool blockUnknownConnections = false;
bool streamerMode = false; bool streamerMode = false;
String _downloadPath = ""; String _downloadPath = "";
@ -94,6 +109,9 @@ class Settings extends ChangeNotifier {
_uiColumnModePortrait = uiColumnModeFromString(settings["UIColumnModePortrait"]); _uiColumnModePortrait = uiColumnModeFromString(settings["UIColumnModePortrait"]);
_uiColumnModeLandscape = uiColumnModeFromString(settings["UIColumnModeLandscape"]); _uiColumnModeLandscape = uiColumnModeFromString(settings["UIColumnModeLandscape"]);
_notificationPolicy = notificationPolicyFromString(settings["NotificationPolicy"]);
_notificationContent = notificationContentFromString(settings["NotificationContent"]);
// auto-download folder // auto-download folder
_downloadPath = settings["DownloadPath"] ?? ""; _downloadPath = settings["DownloadPath"] ?? "";
@ -173,17 +191,33 @@ class Settings extends ChangeNotifier {
} }
DualpaneMode get uiColumnModePortrait => _uiColumnModePortrait; DualpaneMode get uiColumnModePortrait => _uiColumnModePortrait;
set uiColumnModePortrait(DualpaneMode newval) { set uiColumnModePortrait(DualpaneMode newval) {
this._uiColumnModePortrait = newval; this._uiColumnModePortrait = newval;
notifyListeners(); notifyListeners();
} }
DualpaneMode get uiColumnModeLandscape => _uiColumnModeLandscape; DualpaneMode get uiColumnModeLandscape => _uiColumnModeLandscape;
set uiColumnModeLandscape(DualpaneMode newval) { set uiColumnModeLandscape(DualpaneMode newval) {
this._uiColumnModeLandscape = newval; this._uiColumnModeLandscape = newval;
notifyListeners(); notifyListeners();
} }
NotificationPolicy get notificationPolicy => _notificationPolicy;
set notificationPolicy(NotificationPolicy newpol) {
this._notificationPolicy = newpol;
notifyListeners();
}
NotificationContent get notificationContent => _notificationContent;
set notificationContent(NotificationContent newcon) {
this._notificationContent = newcon;
notifyListeners();
}
List<int> uiColumns(bool isLandscape) { List<int> uiColumns(bool isLandscape) {
var m = (!isLandscape || uiColumnModeLandscape == DualpaneMode.CopyPortrait) ? uiColumnModePortrait : uiColumnModeLandscape; var m = (!isLandscape || uiColumnModeLandscape == DualpaneMode.CopyPortrait) ? uiColumnModePortrait : uiColumnModeLandscape;
switch (m) { switch (m) {
@ -238,6 +272,43 @@ class Settings extends ChangeNotifier {
} }
} }
static NotificationPolicy notificationPolicyFromString(String? np) {
switch (np) {
case "NotificationPolicy.None":
return NotificationPolicy.Mute;
case "NotificationPolicy.OptIn":
return NotificationPolicy.OptIn;
case "NotificationPolicy.OptOut":
return NotificationPolicy.DefaultAll;
}
return NotificationPolicy.DefaultAll;
}
static NotificationContent notificationContentFromString(String? nc) {
switch (nc) {
case "NotificationContent.SimpleEvent":
return NotificationContent.SimpleEvent;
case "NotificationContent.ContactInfo":
return NotificationContent.ContactInfo;
}
return NotificationContent.SimpleEvent;
}
static String notificationPolicyToString(NotificationPolicy np, BuildContext context) {
switch (np) {
case NotificationPolicy.Mute: return AppLocalizations.of(context)!.notificationPolicyMute;
case NotificationPolicy.OptIn: return AppLocalizations.of(context)!.notificationPolicyOptIn;
case NotificationPolicy.DefaultAll: return AppLocalizations.of(context)!.notificationPolicyDefaultAll;
}
}
static String notificationContentToString(NotificationContent nc, BuildContext context) {
switch (nc) {
case NotificationContent.SimpleEvent: return AppLocalizations.of(context)!.notificationContentSimpleEvent;
case NotificationContent.ContactInfo: return AppLocalizations.of(context)!.notificationContentContactInfo;
}
}
// checks experiment settings and file extension for image previews // checks experiment settings and file extension for image previews
// (ignores file size; if the user manually accepts the file, assume it's okay to preview) // (ignores file size; if the user manually accepts the file, assume it's okay to preview)
bool shouldPreview(String path) { bool shouldPreview(String path) {
@ -247,18 +318,21 @@ class Settings extends ChangeNotifier {
} }
String get downloadPath => _downloadPath; String get downloadPath => _downloadPath;
set downloadPath(String newval) { set downloadPath(String newval) {
_downloadPath = newval; _downloadPath = newval;
notifyListeners(); notifyListeners();
} }
bool get allowAdvancedTorConfig => _allowAdvancedTorConfig; bool get allowAdvancedTorConfig => _allowAdvancedTorConfig;
set allowAdvancedTorConfig(bool torConfig) { set allowAdvancedTorConfig(bool torConfig) {
_allowAdvancedTorConfig = torConfig; _allowAdvancedTorConfig = torConfig;
notifyListeners(); notifyListeners();
} }
bool get useTorCache => _useTorCache; bool get useTorCache => _useTorCache;
set useTorCache(bool useTorCache) { set useTorCache(bool useTorCache) {
_useTorCache = useTorCache; _useTorCache = useTorCache;
notifyListeners(); notifyListeners();
@ -266,18 +340,21 @@ class Settings extends ChangeNotifier {
// Settings / Gettings for setting the custom tor config.. // Settings / Gettings for setting the custom tor config..
String get torConfig => _customTorConfig; String get torConfig => _customTorConfig;
set torConfig(String torConfig) { set torConfig(String torConfig) {
_customTorConfig = torConfig; _customTorConfig = torConfig;
notifyListeners(); notifyListeners();
} }
int get socksPort => _socksPort; int get socksPort => _socksPort;
set socksPort(int newSocksPort) { set socksPort(int newSocksPort) {
_socksPort = newSocksPort; _socksPort = newSocksPort;
notifyListeners(); notifyListeners();
} }
int get controlPort => _controlPort; int get controlPort => _controlPort;
set controlPort(int controlPort) { set controlPort(int controlPort) {
_controlPort = controlPort; _controlPort = controlPort;
notifyListeners(); notifyListeners();
@ -285,6 +362,7 @@ class Settings extends ChangeNotifier {
// Setters / Getters for toggling whether the app should use a custom tor config // Setters / Getters for toggling whether the app should use a custom tor config
bool get useCustomTorConfig => _useCustomTorConfig; bool get useCustomTorConfig => _useCustomTorConfig;
set useCustomTorConfig(bool useCustomTorConfig) { set useCustomTorConfig(bool useCustomTorConfig) {
_useCustomTorConfig = useCustomTorConfig; _useCustomTorConfig = useCustomTorConfig;
notifyListeners(); notifyListeners();
@ -302,6 +380,8 @@ class Settings extends ChangeNotifier {
"ThemeMode": theme.mode, "ThemeMode": theme.mode,
"PreviousPid": -1, "PreviousPid": -1,
"BlockUnknownConnections": blockUnknownConnections, "BlockUnknownConnections": blockUnknownConnections,
"NotificationPolicy": _notificationPolicy.toString(),
"NotificationContent": _notificationContent.toString(),
"StreamerMode": streamerMode, "StreamerMode": streamerMode,
"ExperimentsEnabled": this.experimentsEnabled, "ExperimentsEnabled": this.experimentsEnabled,
"Experiments": experiments, "Experiments": experiments,

View File

@ -53,11 +53,13 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
isAlwaysShown: true, isAlwaysShown: true,
child: SingleChildScrollView( child: SingleChildScrollView(
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
padding: EdgeInsets.all(20),
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight, minHeight: viewportConstraints.maxHeight,
), ),
child: Column(children: [ child: Column(children: [
Row(mainAxisAlignment: MainAxisAlignment.center, children: [Text(AppLocalizations.of(context)!.settingsGroupAppearance, style: TextStyle(fontWeight: FontWeight.bold))]),
ListTile( ListTile(
title: Text(AppLocalizations.of(context)!.settingLanguage, style: TextStyle(color: settings.current().mainTextColor)), title: Text(AppLocalizations.of(context)!.settingLanguage, style: TextStyle(color: settings.current().mainTextColor)),
leading: Icon(CwtchIcons.change_language, color: settings.current().mainTextColor), leading: Icon(CwtchIcons.change_language, color: settings.current().mainTextColor),
@ -135,24 +137,78 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
style: TextStyle(color: settings.current().mainTextColor), style: TextStyle(color: settings.current().mainTextColor),
), ),
leading: Icon(Icons.table_chart, color: settings.current().mainTextColor), leading: Icon(Icons.table_chart, color: settings.current().mainTextColor),
trailing: Container( trailing: DropdownButton(
width: MediaQuery.of(context).size.width / 4, value: settings.uiColumnModeLandscape.toString(),
child: DropdownButton( onChanged: (String? newValue) {
isExpanded: true, settings.uiColumnModeLandscape = Settings.uiColumnModeFromString(newValue!);
value: settings.uiColumnModeLandscape.toString(), saveSettings(context);
onChanged: (String? newValue) { },
settings.uiColumnModeLandscape = Settings.uiColumnModeFromString(newValue!); items: Settings.uiColumnModeOptions(true).map<DropdownMenuItem<String>>((DualpaneMode value) {
saveSettings(context); return DropdownMenuItem<String>(
}, value: value.toString(),
items: Settings.uiColumnModeOptions(true).map<DropdownMenuItem<String>>((DualpaneMode value) { child: Text(
return DropdownMenuItem<String>( Settings.uiColumnModeToString(value, context),
value: value.toString(), overflow: TextOverflow.ellipsis,
child: Text( ),
Settings.uiColumnModeToString(value, context), );
overflow: TextOverflow.ellipsis, }).toList())),
), SwitchListTile(
); title: Text(AppLocalizations.of(context)!.streamerModeLabel, style: TextStyle(color: settings.current().mainTextColor)),
}).toList()))), subtitle: Text(AppLocalizations.of(context)!.descriptionStreamerMode),
value: settings.streamerMode,
onChanged: (bool value) {
settings.setStreamerMode(value);
// Save Settings...
saveSettings(context);
},
activeTrackColor: settings.theme.defaultButtonColor,
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
secondary: Icon(CwtchIcons.streamer_bunnymask, color: settings.current().mainTextColor),
),
SizedBox(
height: 40,
),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [Text(AppLocalizations.of(context)!.settingGroupBehaviour, style: TextStyle(fontWeight: FontWeight.bold))]),
ListTile(
title: Text(AppLocalizations.of(context)!.notificationPolicySettingLabel),
subtitle: Text(AppLocalizations.of(context)!.notificationPolicySettingDescription),
trailing: DropdownButton(
value: settings.notificationPolicy,
onChanged: (NotificationPolicy? newValue) {
settings.notificationPolicy = newValue!;
saveSettings(context);
},
items: NotificationPolicy.values.map<DropdownMenuItem<NotificationPolicy>>((NotificationPolicy value) {
return DropdownMenuItem<NotificationPolicy>(
value: value,
child: Text(
Settings.notificationPolicyToString(value, context),
overflow: TextOverflow.ellipsis,
),
);
}).toList()),
leading: Icon(CwtchIcons.chat_bubble_empty_24px, color: settings.current().mainTextColor),
),
ListTile(
title: Text(AppLocalizations.of(context)!.notificationContentSettingLabel),
subtitle: Text(AppLocalizations.of(context)!.notificationContentSettingDescription),
trailing: DropdownButton(
value: settings.notificationContent,
onChanged: (NotificationContent? newValue) {
settings.notificationContent = newValue!;
saveSettings(context);
},
items: NotificationContent.values.map<DropdownMenuItem<NotificationContent>>((NotificationContent value) {
return DropdownMenuItem<NotificationContent>(
value: value,
child: Text(
Settings.notificationContentToString(value, context),
overflow: TextOverflow.ellipsis,
),
);
}).toList()),
leading: Icon(CwtchIcons.chat_bubble_empty_24px, color: settings.current().mainTextColor),
),
SwitchListTile( SwitchListTile(
title: Text(AppLocalizations.of(context)!.blockUnknownLabel, style: TextStyle(color: settings.current().mainTextColor)), title: Text(AppLocalizations.of(context)!.blockUnknownLabel, style: TextStyle(color: settings.current().mainTextColor)),
subtitle: Text(AppLocalizations.of(context)!.descriptionBlockUnknownConnections), subtitle: Text(AppLocalizations.of(context)!.descriptionBlockUnknownConnections),
@ -171,19 +227,10 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
inactiveTrackColor: settings.theme.defaultButtonDisabledColor, inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
secondary: Icon(CwtchIcons.block_unknown, color: settings.current().mainTextColor), secondary: Icon(CwtchIcons.block_unknown, color: settings.current().mainTextColor),
), ),
SwitchListTile( SizedBox(
title: Text(AppLocalizations.of(context)!.streamerModeLabel, style: TextStyle(color: settings.current().mainTextColor)), height: 40,
subtitle: Text(AppLocalizations.of(context)!.descriptionStreamerMode),
value: settings.streamerMode,
onChanged: (bool value) {
settings.setStreamerMode(value);
// Save Settings...
saveSettings(context);
},
activeTrackColor: settings.theme.defaultButtonColor,
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
secondary: Icon(CwtchIcons.streamer_bunnymask, color: settings.current().mainTextColor),
), ),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [Text(AppLocalizations.of(context)!.settingsGroupExperiments, style: TextStyle(fontWeight: FontWeight.bold))]),
SwitchListTile( SwitchListTile(
title: Text(AppLocalizations.of(context)!.experimentsEnabled, style: TextStyle(color: settings.current().mainTextColor)), title: Text(AppLocalizations.of(context)!.experimentsEnabled, style: TextStyle(color: settings.current().mainTextColor)),
subtitle: Text(AppLocalizations.of(context)!.descriptionExperiments), subtitle: Text(AppLocalizations.of(context)!.descriptionExperiments),

View File

@ -130,7 +130,26 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
SizedBox( SizedBox(
height: 20, height: 20,
), ),
// TODO ListTile(
title: Text(AppLocalizations.of(context)!.conversationNotificationPolicySettingLabel, style: TextStyle(color: settings.current().mainTextColor)),
subtitle: Text(AppLocalizations.of(context)!.conversationNotificationPolicySettingDescription),
leading: Icon(CwtchIcons.chat_bubble_empty_24px, color: settings.current().mainTextColor),
trailing: DropdownButton(
value: Provider.of<ContactInfoState>(context).notificationsPolicy,
items: ConversationNotificationPolicy.values.map<DropdownMenuItem<ConversationNotificationPolicy>>((ConversationNotificationPolicy value) {
return DropdownMenuItem<ConversationNotificationPolicy>(
value: value,
child: Text(value.toName(context)),
);
}).toList(),
onChanged: (ConversationNotificationPolicy? newVal) {
Provider.of<ContactInfoState>(context, listen: false).notificationsPolicy = newVal!;
var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
var identifier = Provider.of<ContactInfoState>(context, listen: false).identifier;
const NotificationPolicyKey = "profile.notification-policy";
Provider.of<FlwtchState>(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, NotificationPolicyKey, newVal.toString());
},
)),
]), ]),
Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [ Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [

View File

@ -40,9 +40,13 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var handle = Provider.of<ContactInfoState>(context).nickname;
if (handle.isEmpty) {
handle = Provider.of<ContactInfoState>(context).onion;
}
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(Provider.of<ContactInfoState>(context).onion), title: Text(handle + " " + AppLocalizations.of(context)!.conversationSettings),
), ),
body: _buildSettingsList(), body: _buildSettingsList(),
); );
@ -211,6 +215,26 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
child: Text(value), child: Text(value),
); );
}).toList())), }).toList())),
ListTile(
title: Text(AppLocalizations.of(context)!.conversationNotificationPolicySettingLabel, style: TextStyle(color: settings.current().mainTextColor)),
subtitle: Text(AppLocalizations.of(context)!.conversationNotificationPolicySettingDescription),
leading: Icon(CwtchIcons.chat_bubble_empty_24px, color: settings.current().mainTextColor),
trailing: DropdownButton(
value: Provider.of<ContactInfoState>(context).notificationsPolicy,
items: ConversationNotificationPolicy.values.map<DropdownMenuItem<ConversationNotificationPolicy>>((ConversationNotificationPolicy value) {
return DropdownMenuItem<ConversationNotificationPolicy>(
value: value,
child: Text(value.toName(context)),
);
}).toList(),
onChanged: (ConversationNotificationPolicy? newVal) {
Provider.of<ContactInfoState>(context, listen: false).notificationsPolicy = newVal!;
var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
var identifier = Provider.of<ContactInfoState>(context, listen: false).identifier;
const NotificationPolicyKey = "profile.notification-policy";
Provider.of<FlwtchState>(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, NotificationPolicyKey, newVal.toString());
},
)),
]), ]),
Column(mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [ Column(mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [
SizedBox( SizedBox(

View File

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../main.dart';
import '../settings.dart'; import '../settings.dart';
class SplashView extends StatefulWidget { class SplashView extends StatefulWidget {
@ -14,6 +15,13 @@ class SplashView extends StatefulWidget {
class _SplashViewState extends State<SplashView> { class _SplashViewState extends State<SplashView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var cwtch = Provider.of<FlwtchState>(context, listen: false).cwtch;
if (!cwtch.isL10nInit()) {
if (AppLocalizations.of(context) != null && AppLocalizations.of(context)!.newMessageNotificationSimple.isNotEmpty ) {
cwtch.l10nInit(AppLocalizations.of(context)!.newMessageNotificationSimple, AppLocalizations.of(context)!.newMessageNotificationConversationInfo);
}
}
return Consumer<AppState>( return Consumer<AppState>(
builder: (context, appState, child) => Scaffold( builder: (context, appState, child) => Scaffold(
key: Key("SplashView"), key: Key("SplashView"),