server manager

This commit is contained in:
Dan Ballard 2021-10-29 16:37:02 -07:00
parent d4b9f1dc55
commit 9789a42e94
22 changed files with 173 additions and 83 deletions

View File

@ -314,7 +314,9 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
} }
"CreateServer" -> { "CreateServer" -> {
val password = (a.get("Password") as? String) ?: "" val password = (a.get("Password") as? String) ?: ""
Cwtch.createServer(password) val desc = (a.get("Description") as? String) ?: ""
val autostart = (a.get("Autostart") as? Byte) ?: ""
Cwtch.createServer(password, desc, autostart)
} }
"DeleteServer" -> { "DeleteServer" -> {
val serverOnion = (a.get("ServerOnion") as? String) ?: "" val serverOnion = (a.get("ServerOnion") as? String) ?: ""
@ -335,6 +337,12 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
"ShutdownServers" -> { "ShutdownServers" -> {
Cwtch.shutdownServers() Cwtch.shutdownServers()
} }
"SetServerAttribute" -> {
val serverOnion = (a.get("ServerOnion") as? String) ?: ""
val key = (a.get("Key") as? String) ?: ""
val v = (a.get("Val") as? String) ?: ""
Cwtch.setServerAttribute(serverOnion, key, v)
}
else -> return Result.failure() else -> return Result.failure()
} }
return Result.success() return Result.success()

View File

@ -1,5 +1,7 @@
import 'package:flutter/src/services/text_input.dart'; import 'package:flutter/src/services/text_input.dart';
const DefaultPassword = "be gay do crime";
abstract class Cwtch { abstract class Cwtch {
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
Future<void> Start(); Future<void> Start();
@ -71,7 +73,7 @@ abstract class Cwtch {
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
void LoadServers(String password); void LoadServers(String password);
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
void CreateServer(String password); void CreateServer(String password, String description, bool autostart);
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
void DeleteServer(String serverOnion, String password); void DeleteServer(String serverOnion, String password);
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
@ -82,6 +84,8 @@ abstract class Cwtch {
void ShutdownServer(String serverOnion); void ShutdownServer(String serverOnion);
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
void ShutdownServers(); void ShutdownServers();
// ignore: non_constant_identifier_names
void SetServerAttribute(String serverOnion, String key, String val);
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
void Shutdown(); void Shutdown();

View File

@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:cwtch/models/message.dart'; import 'package:cwtch/models/message.dart';
import 'package:cwtch/models/profileservers.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:provider/provider.dart'; import 'package:provider/provider.dart';
@ -20,17 +21,20 @@ class CwtchNotifier {
late TorStatus torStatus; late TorStatus torStatus;
late NotificationsManager notificationManager; late NotificationsManager notificationManager;
late AppState appState; late AppState appState;
late ServerListState serverListState;
CwtchNotifier(ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN, TorStatus torStatusCN, NotificationsManager notificationManagerP, AppState appStateCN) { CwtchNotifier(ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN, TorStatus torStatusCN, NotificationsManager notificationManagerP, AppState appStateCN, ServerListState serverListStateCN) {
profileCN = pcn; profileCN = pcn;
settings = settingsCN; settings = settingsCN;
error = errorCN; error = errorCN;
torStatus = torStatusCN; torStatus = torStatusCN;
notificationManager = notificationManagerP; notificationManager = notificationManagerP;
appState = appStateCN; appState = appStateCN;
serverListState = serverListStateCN;
} }
void handleMessage(String type, dynamic data) { void handleMessage(String type, dynamic data) {
print("EVENT $type $data");
switch (type) { switch (type) {
case "CwtchStarted": case "CwtchStarted":
appState.SetCwtchInit(); appState.SetCwtchInit();
@ -59,11 +63,21 @@ class CwtchNotifier {
lastMessageTime: DateTime.now(), //show at the top of the contact list even if no messages yet lastMessageTime: DateTime.now(), //show at the top of the contact list even if no messages yet
)); ));
break; break;
case "NewServer":
var serverData = jsonDecode(data["Data"]);
serverListState.add(
serverData["onion"],
serverData["serverbundle"],
serverData["enabled"] == "true",
serverData["description"],
serverData["autostart"] == "true",
serverData["storageType"] == "storage-password");
break;
case "GroupCreated": case "GroupCreated":
// Retrieve Server Status from Cache... // Retrieve Server Status from Cache...
String status = ""; String status = "";
ServerInfoState? serverInfoState = profileCN.getProfile(data["ProfileOnion"])?.serverList.getServer(data["GroupServer"]); RemoteServerInfoState? serverInfoState = profileCN.getProfile(data["ProfileOnion"])?.serverList.getServer(data["GroupServer"]);
if (serverInfoState != null) { if (serverInfoState != null) {
status = serverInfoState.status; status = serverInfoState.status;
} }
@ -263,7 +277,7 @@ class CwtchNotifier {
// Retrieve Server Status from Cache... // Retrieve Server Status from Cache...
String status = ""; String status = "";
ServerInfoState? serverInfoState = profileCN.getProfile(data["ProfileOnion"])!.serverList.getServer(groupInvite["ServerHost"]); RemoteServerInfoState? serverInfoState = profileCN.getProfile(data["ProfileOnion"])!.serverList.getServer(groupInvite["ServerHost"]);
if (serverInfoState != null) { if (serverInfoState != null) {
status = serverInfoState.status; status = serverInfoState.status;
} }

View File

@ -42,6 +42,9 @@ typedef VoidFromStringStringStringStringStringFn = void Function(Pointer<Utf8>,
typedef void_from_string_string_int_int_function = Void Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Int64, Int64); typedef void_from_string_string_int_int_function = Void Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Int64, Int64);
typedef VoidFromStringStringIntIntFn = void Function(Pointer<Utf8>, int, Pointer<Utf8>, int, int, int); typedef VoidFromStringStringIntIntFn = void Function(Pointer<Utf8>, int, Pointer<Utf8>, int, int, int);
typedef void_from_string_string_byte_function = Void Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Int8);
typedef VoidFromStringStringByteFn = void Function(Pointer<Utf8>, int, Pointer<Utf8>, int, int);
typedef string_to_void_function = Void Function(Pointer<Utf8> str, Int32 length); typedef string_to_void_function = Void Function(Pointer<Utf8> str, Int32 length);
typedef StringFn = void Function(Pointer<Utf8> dir, int); typedef StringFn = void Function(Pointer<Utf8> dir, int);
@ -575,13 +578,15 @@ class CwtchFfi implements Cwtch {
@override @override
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
void CreateServer(String password) { void CreateServer(String password, String description, bool autostart) {
var createServer = library.lookup<NativeFunction<string_to_void_function>>("c_CreateServer"); var createServer = library.lookup<NativeFunction<void_from_string_string_byte_function>>("c_CreateServer");
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
final CreateServer = createServer.asFunction<StringFn>(); final CreateServer = createServer.asFunction<VoidFromStringStringByteFn>();
final u1 = password.toNativeUtf8(); final u1 = password.toNativeUtf8();
CreateServer(u1, u1.length); final u2 = description.toNativeUtf8();
CreateServer(u1, u1.length, u2, u2.length, autostart ? 1 : 0);
malloc.free(u1); malloc.free(u1);
malloc.free(u2);
} }
@override @override
@ -637,6 +642,21 @@ class CwtchFfi implements Cwtch {
ShutdownServers(); ShutdownServers();
} }
@override
// ignore: non_constant_identifier_names
void SetServerAttribute(String serverOnion, String key, String val) {
var setServerAttribute = library.lookup<NativeFunction<void_from_string_string_string_function>>("c_SetServerAttribute");
// ignore: non_constant_identifier_names
final SetServerAttribute = setServerAttribute.asFunction<VoidFromStringStringStringFn>();
final u1 = serverOnion.toNativeUtf8();
final u2 = key.toNativeUtf8();
final u3 = val.toNativeUtf8();
SetServerAttribute(u1, u1.length, u2, u2.length, u3, u3.length);
malloc.free(u1);
malloc.free(u2);
malloc.free(u3);
}
@override @override
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
Future<void> Shutdown() async { Future<void> Shutdown() async {

View File

@ -221,8 +221,8 @@ class CwtchGomobile implements Cwtch {
@override @override
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
void CreateServer(String password) { void CreateServer(String password, String description, bool autostart) {
cwtchPlatform.invokeMethod("CreateServer", {"Password": password}); cwtchPlatform.invokeMethod("CreateServer", {"Password": password, "Description": description, "Autostart": autostart ? 1 : 0});
} }
@override @override
@ -255,6 +255,12 @@ class CwtchGomobile implements Cwtch {
cwtchPlatform.invokeMethod("ShutdownServers", {}); cwtchPlatform.invokeMethod("ShutdownServers", {});
} }
@override
// ignore: non_constant_identifier_names
void SetServerAttribute(String serverOnion, String key, String val) {
cwtchPlatform.invokeMethod("SetServerAttribute", {"ServerOnion": serverOnion, "Key": key, "Val": val});
}
@override @override
Future<void> Shutdown() async { Future<void> Shutdown() async {
print("gomobile.dart Shutdown"); print("gomobile.dart Shutdown");

View File

@ -1,6 +1,8 @@
{ {
"@@locale": "de", "@@locale": "de",
"@@last_modified": "2021-09-21T23:09:19+02:00", "@@last_modified": "2021-10-15T20:45:29+02:00",
"titleManageProfilesShort": "Profiles",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.",
"settingFileSharing": "File Sharing", "settingFileSharing": "File Sharing",
"tooltipSendFile": "Send File", "tooltipSendFile": "Send File",
@ -12,7 +14,6 @@
"downloadFileButton": "Download", "downloadFileButton": "Download",
"openFolderButton": "Open Folder", "openFolderButton": "Open Folder",
"retrievingManifestMessage": "Retrieving file information...", "retrievingManifestMessage": "Retrieving file information...",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions",
"streamerModeLabel": "Streamer\/Presentation Mode", "streamerModeLabel": "Streamer\/Presentation Mode",
"archiveConversation": "Archive this Conversation", "archiveConversation": "Archive this Conversation",
"profileOnionLabel": "Senden Sie diese Adresse an Peers, mit denen Sie sich verbinden möchten", "profileOnionLabel": "Senden Sie diese Adresse an Peers, mit denen Sie sich verbinden möchten",

View File

@ -1,6 +1,8 @@
{ {
"@@locale": "en", "@@locale": "en",
"@@last_modified": "2021-09-21T23:09:19+02:00", "@@last_modified": "2021-10-15T20:45:29+02:00",
"titleManageProfilesShort": "Profiles",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.",
"settingFileSharing": "File Sharing", "settingFileSharing": "File Sharing",
"tooltipSendFile": "Send File", "tooltipSendFile": "Send File",
@ -12,7 +14,6 @@
"downloadFileButton": "Download", "downloadFileButton": "Download",
"openFolderButton": "Open Folder", "openFolderButton": "Open Folder",
"retrievingManifestMessage": "Retrieving file information...", "retrievingManifestMessage": "Retrieving file information...",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions",
"streamerModeLabel": "Streamer\/Presentation Mode", "streamerModeLabel": "Streamer\/Presentation Mode",
"archiveConversation": "Archive this Conversation", "archiveConversation": "Archive this Conversation",
"profileOnionLabel": "Send this address to people you want to connect with", "profileOnionLabel": "Send this address to people you want to connect with",

View File

@ -1,6 +1,8 @@
{ {
"@@locale": "es", "@@locale": "es",
"@@last_modified": "2021-09-21T23:09:19+02:00", "@@last_modified": "2021-10-15T20:45:29+02:00",
"titleManageProfilesShort": "Profiles",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.",
"settingFileSharing": "File Sharing", "settingFileSharing": "File Sharing",
"tooltipSendFile": "Send File", "tooltipSendFile": "Send File",
@ -12,7 +14,6 @@
"downloadFileButton": "Download", "downloadFileButton": "Download",
"openFolderButton": "Open Folder", "openFolderButton": "Open Folder",
"retrievingManifestMessage": "Retrieving file information...", "retrievingManifestMessage": "Retrieving file information...",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions",
"streamerModeLabel": "Streamer\/Presentation Mode", "streamerModeLabel": "Streamer\/Presentation Mode",
"archiveConversation": "Archive this Conversation", "archiveConversation": "Archive this Conversation",
"profileOnionLabel": "Envía esta dirección a los contactos con los que quieras conectarte", "profileOnionLabel": "Envía esta dirección a los contactos con los que quieras conectarte",

View File

@ -1,18 +1,19 @@
{ {
"@@locale": "fr", "@@locale": "fr",
"@@last_modified": "2021-09-21T23:09:19+02:00", "@@last_modified": "2021-10-15T20:45:29+02:00",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", "titleManageProfilesShort": "Profiles",
"settingFileSharing": "File Sharing", "descriptionStreamerMode": "Si elle est activée, cette option donne un rendu visuel plus privé à l'application pour la diffusion en direct ou la présentation, par exemple, en masquant profil et adresses de contacts.",
"tooltipSendFile": "Send File", "descriptionFileSharing": "L'expérience de partage de fichiers vous permet d'envoyer et de recevoir des fichiers à partir de contacts et de groupes Cwtch. Notez que si vous partagez un fichier avec un groupe, les membres de ce groupe se connecteront avec vous directement via Cwtch pour le télécharger.",
"messageFileOffered": "Contact is offering to send you a file", "settingFileSharing": "Partage de fichiers",
"messageFileSent": "You sent a file", "tooltipSendFile": "Envoyer le fichier",
"messageEnableFileSharing": "Enable the file sharing experiment to view this message.", "messageFileOffered": "Contact vous propose de vous envoyer un fichier",
"labelFilesize": "Size", "messageFileSent": "Vous avez envoyé un fichier",
"labelFilename": "Filename", "messageEnableFileSharing": "Activez l'expérience de partage de fichiers pour afficher ce message.",
"downloadFileButton": "Download", "labelFilesize": "Taille",
"openFolderButton": "Open Folder", "labelFilename": "Nom de fichier",
"retrievingManifestMessage": "Retrieving file information...", "downloadFileButton": "Télécharger",
"descriptionStreamerMode": "Si elle est activée, cette option donne un rendu visuel plus privé à l'application pour la diffusion ou la présentation, par exemple en masquant les profils et les contacts.", "openFolderButton": "Ouvrir le dossier",
"retrievingManifestMessage": "Récupération des informations sur le fichier...",
"streamerModeLabel": "Mode Streamer\/Présentation", "streamerModeLabel": "Mode Streamer\/Présentation",
"archiveConversation": "Archiver cette conversation", "archiveConversation": "Archiver cette conversation",
"profileOnionLabel": "Envoyez cette adresse aux personnes avec lesquelles vous souhaitez entrer en contact.", "profileOnionLabel": "Envoyez cette adresse aux personnes avec lesquelles vous souhaitez entrer en contact.",

View File

@ -1,10 +1,12 @@
{ {
"@@locale": "it", "@@locale": "it",
"@@last_modified": "2021-09-21T23:09:19+02:00", "@@last_modified": "2021-10-15T20:45:29+02:00",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", "titleManageProfilesShort": "Profiles",
"settingFileSharing": "File Sharing", "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses",
"tooltipSendFile": "Send File", "descriptionFileSharing": "L'esperimento di condivisione dei file ti consente di inviare e ricevere file dai contatti e dai gruppi di Cwtch. Tieni presente che la condivisione di un file con un gruppo farà sì che i membri di quel gruppo si colleghino con te direttamente su Cwtch per scaricarlo.",
"messageFileOffered": "Contact is offering to send you a file", "settingFileSharing": "Condivisione file",
"tooltipSendFile": "Invia file",
"messageFileOffered": "Il contatto offre l'invio di un file",
"messageFileSent": "You sent a file", "messageFileSent": "You sent a file",
"messageEnableFileSharing": "Enable the file sharing experiment to view this message.", "messageEnableFileSharing": "Enable the file sharing experiment to view this message.",
"labelFilesize": "Size", "labelFilesize": "Size",
@ -12,7 +14,6 @@
"downloadFileButton": "Download", "downloadFileButton": "Download",
"openFolderButton": "Open Folder", "openFolderButton": "Open Folder",
"retrievingManifestMessage": "Retrieving file information...", "retrievingManifestMessage": "Retrieving file information...",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions",
"streamerModeLabel": "Streamer\/Presentation Mode", "streamerModeLabel": "Streamer\/Presentation Mode",
"archiveConversation": "Archive this Conversation", "archiveConversation": "Archive this Conversation",
"profileOnionLabel": "Inviare questo indirizzo ai peer con cui si desidera connettersi", "profileOnionLabel": "Inviare questo indirizzo ai peer con cui si desidera connettersi",

View File

@ -1,6 +1,8 @@
{ {
"@@locale": "pl", "@@locale": "pl",
"@@last_modified": "2021-09-21T23:09:19+02:00", "@@last_modified": "2021-10-15T20:45:29+02:00",
"titleManageProfilesShort": "Profiles",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.",
"settingFileSharing": "File Sharing", "settingFileSharing": "File Sharing",
"tooltipSendFile": "Send File", "tooltipSendFile": "Send File",
@ -12,7 +14,6 @@
"downloadFileButton": "Download", "downloadFileButton": "Download",
"openFolderButton": "Open Folder", "openFolderButton": "Open Folder",
"retrievingManifestMessage": "Retrieving file information...", "retrievingManifestMessage": "Retrieving file information...",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions",
"streamerModeLabel": "Streamer\/Presentation Mode", "streamerModeLabel": "Streamer\/Presentation Mode",
"archiveConversation": "Archive this Conversation", "archiveConversation": "Archive this Conversation",
"profileOnionLabel": "Send this address to contacts you want to connect with", "profileOnionLabel": "Send this address to contacts you want to connect with",

View File

@ -1,6 +1,8 @@
{ {
"@@locale": "pt", "@@locale": "pt",
"@@last_modified": "2021-09-21T23:09:19+02:00", "@@last_modified": "2021-10-15T20:45:29+02:00",
"titleManageProfilesShort": "Profiles",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.",
"settingFileSharing": "File Sharing", "settingFileSharing": "File Sharing",
"tooltipSendFile": "Send File", "tooltipSendFile": "Send File",
@ -12,7 +14,6 @@
"downloadFileButton": "Download", "downloadFileButton": "Download",
"openFolderButton": "Open Folder", "openFolderButton": "Open Folder",
"retrievingManifestMessage": "Retrieving file information...", "retrievingManifestMessage": "Retrieving file information...",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions",
"streamerModeLabel": "Streamer\/Presentation Mode", "streamerModeLabel": "Streamer\/Presentation Mode",
"archiveConversation": "Archive this Conversation", "archiveConversation": "Archive this Conversation",
"profileOnionLabel": "Send this address to contacts you want to connect with", "profileOnionLabel": "Send this address to contacts you want to connect with",

View File

@ -17,6 +17,7 @@ import 'cwtch/cwtch.dart';
import 'cwtch/cwtchNotifier.dart'; import 'cwtch/cwtchNotifier.dart';
import 'licenses.dart'; import 'licenses.dart';
import 'model.dart'; import 'model.dart';
import 'models/servers.dart';
import 'views/profilemgrview.dart'; import 'views/profilemgrview.dart';
import 'views/splashView.dart'; import 'views/splashView.dart';
import 'dart:io' show Platform, exit; import 'dart:io' show Platform, exit;
@ -27,6 +28,7 @@ var globalSettings = Settings(Locale("en", ''), OpaqueDark());
var globalErrorHandler = ErrorHandler(); var globalErrorHandler = ErrorHandler();
var globalTorStatus = TorStatus(); var globalTorStatus = TorStatus();
var globalAppState = AppState(); var globalAppState = AppState();
var globalServersList = ServerListState();
void main() { void main() {
print("Cwtch version: ${EnvironmentConfig.BUILD_VER} built on: ${EnvironmentConfig.BUILD_DATE}"); print("Cwtch version: ${EnvironmentConfig.BUILD_VER} built on: ${EnvironmentConfig.BUILD_DATE}");
@ -62,13 +64,13 @@ class FlwtchState extends State<Flwtch> {
shutdownMethodChannel.setMethodCallHandler(modalShutdown); shutdownMethodChannel.setMethodCallHandler(modalShutdown);
print("initState: creating cwtchnotifier, ffi"); print("initState: creating cwtchnotifier, ffi");
if (Platform.isAndroid) { if (Platform.isAndroid) {
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager(), globalAppState); var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager(), globalAppState, globalServersList);
cwtch = CwtchGomobile(cwtchNotifier); cwtch = CwtchGomobile(cwtchNotifier);
} else if (Platform.isLinux) { } else if (Platform.isLinux) {
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(), globalAppState); var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(), globalAppState, globalServersList);
cwtch = CwtchFfi(cwtchNotifier); cwtch = CwtchFfi(cwtchNotifier);
} else { } else {
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager(), globalAppState); var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager(), globalAppState, globalServersList);
cwtch = CwtchFfi(cwtchNotifier); cwtch = CwtchFfi(cwtchNotifier);
} }
print("initState: invoking cwtch.Start()"); print("initState: invoking cwtch.Start()");
@ -82,6 +84,7 @@ class FlwtchState extends State<Flwtch> {
ChangeNotifierProvider<AppState> getAppStateProvider() => ChangeNotifierProvider.value(value: globalAppState); ChangeNotifierProvider<AppState> getAppStateProvider() => ChangeNotifierProvider.value(value: globalAppState);
Provider<FlwtchState> getFlwtchStateProvider() => Provider<FlwtchState>(create: (_) => this); Provider<FlwtchState> getFlwtchStateProvider() => Provider<FlwtchState>(create: (_) => this);
ChangeNotifierProvider<ProfileListState> getProfileListProvider() => ChangeNotifierProvider(create: (context) => profs); ChangeNotifierProvider<ProfileListState> getProfileListProvider() => ChangeNotifierProvider(create: (context) => profs);
ChangeNotifierProvider<ServerListState> getServerListStateProvider() => ChangeNotifierProvider.value(value: globalServersList);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -94,6 +97,7 @@ class FlwtchState extends State<Flwtch> {
getErrorHandlerProvider(), getErrorHandlerProvider(),
getTorStatusProvider(), getTorStatusProvider(),
getAppStateProvider(), getAppStateProvider(),
getServerListStateProvider(),
], ],
builder: (context, widget) { builder: (context, widget) {
return Consumer2<Settings, AppState>( return Consumer2<Settings, AppState>(

View File

@ -2,7 +2,7 @@ import 'dart:convert';
import 'package:cwtch/widgets/messagerow.dart'; import 'package:cwtch/widgets/messagerow.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:cwtch/models/servers.dart'; import 'package:cwtch/models/profileservers.dart';
//////////////////// ////////////////////
/// UI State /// /// UI State ///
@ -207,7 +207,7 @@ class ContactListState extends ChangeNotifier {
class ProfileInfoState extends ChangeNotifier { class ProfileInfoState extends ChangeNotifier {
ContactListState _contacts = ContactListState(); ContactListState _contacts = ContactListState();
ServerListState _servers = ServerListState(); ProfileServerListState _servers = ProfileServerListState();
final String onion; final String onion;
String _nickname = ""; String _nickname = "";
String _imagePath = ""; String _imagePath = "";
@ -267,7 +267,7 @@ class ProfileInfoState extends ChangeNotifier {
List<dynamic> servers = jsonDecode(serversJson); List<dynamic> servers = jsonDecode(serversJson);
this._servers.replace(servers.map((server) { this._servers.replace(servers.map((server) {
// TODO Keys... // TODO Keys...
return ServerInfoState(onion: server["onion"], status: server["status"]); return RemoteServerInfoState(onion: server["onion"], status: server["status"]);
})); }));
notifyListeners(); notifyListeners();
} }
@ -316,7 +316,7 @@ class ProfileInfoState extends ChangeNotifier {
} }
ContactListState get contactList => this._contacts; ContactListState get contactList => this._contacts;
ServerListState get serverList => this._servers; ProfileServerListState get serverList => this._servers;
@override @override
void dispose() { void dispose() {

View File

@ -1,15 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ServerListState extends ChangeNotifier { class ProfileServerListState extends ChangeNotifier {
List<ServerInfoState> _servers = []; List<RemoteServerInfoState> _servers = [];
void replace(Iterable<ServerInfoState> newServers) { void replace(Iterable<RemoteServerInfoState> newServers) {
_servers.clear(); _servers.clear();
_servers.addAll(newServers); _servers.addAll(newServers);
notifyListeners(); notifyListeners();
} }
ServerInfoState? getServer(String onion) { RemoteServerInfoState? getServer(String onion) {
int idx = _servers.indexWhere((element) => element.onion == onion); int idx = _servers.indexWhere((element) => element.onion == onion);
return idx >= 0 ? _servers[idx] : null; return idx >= 0 ? _servers[idx] : null;
} }
@ -17,20 +17,20 @@ class ServerListState extends ChangeNotifier {
void updateServerCache(String onion, String status) { void updateServerCache(String onion, String status) {
int idx = _servers.indexWhere((element) => element.onion == onion); int idx = _servers.indexWhere((element) => element.onion == onion);
if (idx >= 0) { if (idx >= 0) {
_servers[idx] = ServerInfoState(onion: onion, status: status); _servers[idx] = RemoteServerInfoState(onion: onion, status: status);
} else { } else {
print("Tried to update server cache without a starting state...this is probably an error"); print("Tried to update server cache without a starting state...this is probably an error");
} }
notifyListeners(); notifyListeners();
} }
List<ServerInfoState> get servers => _servers.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier List<RemoteServerInfoState> get servers => _servers.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier
} }
class ServerInfoState extends ChangeNotifier { class RemoteServerInfoState extends ChangeNotifier {
final String onion; final String onion;
final String status; final String status;
ServerInfoState({required this.onion, required this.status}); RemoteServerInfoState({required this.onion, required this.status});
} }

View File

@ -9,6 +9,7 @@ import 'opaque.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
const TapirGroupsExperiment = "tapir-groups-experiment"; const TapirGroupsExperiment = "tapir-groups-experiment";
const ServerManagementExperiment = "servers-experiment";
const FileSharingExperiment = "filesharing"; const FileSharingExperiment = "filesharing";
enum DualpaneMode { enum DualpaneMode {

View File

@ -4,7 +4,7 @@ import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:cwtch/errorHandler.dart'; import 'package:cwtch/errorHandler.dart';
import 'package:cwtch/models/servers.dart'; import 'package:cwtch/models/profileservers.dart';
import 'package:cwtch/settings.dart'; import 'package:cwtch/settings.dart';
import 'package:cwtch/widgets/buttontextfield.dart'; import 'package:cwtch/widgets/buttontextfield.dart';
import 'package:cwtch/widgets/cwtchlabel.dart'; import 'package:cwtch/widgets/cwtchlabel.dart';
@ -197,7 +197,7 @@ class _AddContactViewState extends State<AddContactView> {
}, },
isExpanded: true, // magic property isExpanded: true, // magic property
value: server, value: server,
items: Provider.of<ProfileInfoState>(context).serverList.servers.map<DropdownMenuItem<String>>((ServerInfoState serverInfo) { items: Provider.of<ProfileInfoState>(context).serverList.servers.map<DropdownMenuItem<String>>((RemoteServerInfoState serverInfo) {
return DropdownMenuItem<String>( return DropdownMenuItem<String>(
value: serverInfo.onion, value: serverInfo.onion,
child: Text( child: Text(
@ -240,8 +240,8 @@ class _AddContactViewState extends State<AddContactView> {
/// TODO Manage Servers Tab /// TODO Manage Servers Tab
Widget manageServersTab() { Widget manageServersTab() {
final tiles = Provider.of<ProfileInfoState>(context).serverList.servers.map((ServerInfoState server) { final tiles = Provider.of<ProfileInfoState>(context).serverList.servers.map((RemoteServerInfoState server) {
return ChangeNotifierProvider<ServerInfoState>.value( return ChangeNotifierProvider<RemoteServerInfoState>.value(
value: server, value: server,
child: ListTile( child: ListTile(
title: Text(server.onion), title: Text(server.onion),

View File

@ -1,6 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:math'; import 'dart:math';
import 'package:cwtch/cwtch/cwtch.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:cwtch/model.dart'; import 'package:cwtch/model.dart';
@ -106,6 +107,7 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
labelText: AppLocalizations.of(context)!.yourDisplayName, labelText: AppLocalizations.of(context)!.yourDisplayName,
validator: (value) { validator: (value) {
if (value.isEmpty) { if (value.isEmpty) {
// TODO l10n ize
return "Please enter a display name"; return "Please enter a display name";
} }
return null; return null;
@ -287,32 +289,19 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
Provider.of<FlwtchState>(context, listen: false).cwtch.CreateProfile(ctrlrNick.value.text, ctrlrPass.value.text); Provider.of<FlwtchState>(context, listen: false).cwtch.CreateProfile(ctrlrNick.value.text, ctrlrPass.value.text);
Navigator.of(context).pop(); Navigator.of(context).pop();
} else { } else {
Provider.of<FlwtchState>(context, listen: false).cwtch.CreateProfile(ctrlrNick.value.text, "be gay do crime"); Provider.of<FlwtchState>(context, listen: false).cwtch.CreateProfile(ctrlrNick.value.text, DefaultPassword);
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
} else { } else {
// Profile Editing // Profile Editing
if (ctrlrPass.value.text.isEmpty) { if (ctrlrPass.value.text.isEmpty) {
// Don't update password, only update name // Don't update password, only update name
final event = { Provider.of<FlwtchState>(context, listen: false).cwtch.SetProfileAttribute(Provider.of<ProfileInfoState>(context, listen: false).onion, "public.name", ctrlrNick.value.text);
"EventType": "SetAttribute",
"Data": {"Key": "public.name", "Data": ctrlrNick.value.text}
};
final json = jsonEncode(event);
Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(Provider.of<ProfileInfoState>(context, listen: false).onion, json);
Navigator.of(context).pop(); Navigator.of(context).pop();
} else { } else {
// At this points passwords have been validated to be the same and not empty // At this points passwords have been validated to be the same and not empty
// Update both password and name, even if name hasn't been changed... // Update both password and name, even if name hasn't been changed...
final updateNameEvent = { Provider.of<FlwtchState>(context, listen: false).cwtch.SetProfileAttribute(Provider.of<ProfileInfoState>(context, listen: false).onion, "public.name", ctrlrNick.value.text);
"EventType": "SetAttribute",
"Data": {"Key": "public.name", "Data": ctrlrNick.value.text}
};
final updateNameEventJson = jsonEncode(updateNameEvent);
Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(Provider.of<ProfileInfoState>(context, listen: false).onion, updateNameEventJson);
final updatePasswordEvent = { final updatePasswordEvent = {
"EventType": "ChangePassword", "EventType": "ChangePassword",
"Data": {"Password": ctrlrOldPass.text, "NewPassword": ctrlrPass.text} "Data": {"Password": ctrlrOldPass.text, "NewPassword": ctrlrPass.text}

View File

@ -103,9 +103,10 @@ class _ContactsViewState extends State<ContactsView> {
if (Provider.of<Settings>(context).blockUnknownConnections) { if (Provider.of<Settings>(context).blockUnknownConnections) {
actions.add(Tooltip(message: AppLocalizations.of(context)!.blockUnknownConnectionsEnabledDescription, child: Icon(CwtchIcons.block_unknown))); actions.add(Tooltip(message: AppLocalizations.of(context)!.blockUnknownConnectionsEnabledDescription, child: Icon(CwtchIcons.block_unknown)));
} }
actions.add(
IconButton(icon: TorIcon(), onPressed: _pushTorStatus), // TODO copy ID
); // TODO servers
actions.add(IconButton( actions.add(IconButton(
// need both conditions for displaying initial empty textfield and also allowing filters to be cleared if this widget gets lost/reset // need both conditions for displaying initial empty textfield and also allowing filters to be cleared if this widget gets lost/reset
icon: Icon(showSearchBar || Provider.of<ContactListState>(context).isFiltered ? Icons.search_off : Icons.search), icon: Icon(showSearchBar || Provider.of<ContactListState>(context).isFiltered ? Icons.search_off : Icons.search),

View File

@ -188,6 +188,23 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
inactiveTrackColor: settings.theme.defaultButtonDisabledColor(), inactiveTrackColor: settings.theme.defaultButtonDisabledColor(),
secondary: Icon(CwtchIcons.enable_groups, color: settings.current().mainTextColor()), secondary: Icon(CwtchIcons.enable_groups, color: settings.current().mainTextColor()),
), ),
SwitchListTile(
title: Text("Enable Servers", /*AppLocalizations.of(context)!.enableGroups,*/ style: TextStyle(color: settings.current().mainTextColor())),
subtitle: Text("Enable Servers"), //AppLocalizations.of(context)!.descriptionExperimentsGroups),
value: settings.isExperimentEnabled(ServerManagementExperiment),
onChanged: (bool value) {
if (value) {
settings.enableExperiment(ServerManagementExperiment);
} else {
settings.disableExperiment(ServerManagementExperiment);
}
// Save Settings...
saveSettings(context);
},
activeTrackColor: settings.theme.defaultButtonActiveColor(),
inactiveTrackColor: settings.theme.defaultButtonDisabledColor(),
secondary: Icon(CwtchIcons.dns_24px, color: settings.current().mainTextColor()),
),
SwitchListTile( SwitchListTile(
title: Text(AppLocalizations.of(context)!.settingFileSharing, style: TextStyle(color: settings.current().mainTextColor())), title: Text(AppLocalizations.of(context)!.settingFileSharing, style: TextStyle(color: settings.current().mainTextColor())),
subtitle: Text(AppLocalizations.of(context)!.descriptionFileSharing), subtitle: Text(AppLocalizations.of(context)!.descriptionFileSharing),

View File

@ -17,6 +17,7 @@ import '../model.dart';
import '../torstatus.dart'; import '../torstatus.dart';
import 'addeditprofileview.dart'; import 'addeditprofileview.dart';
import 'globalsettingsview.dart'; import 'globalsettingsview.dart';
import 'serversview.dart';
class ProfileMgrView extends StatefulWidget { class ProfileMgrView extends StatefulWidget {
ProfileMgrView(); ProfileMgrView();
@ -56,12 +57,14 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
SizedBox( SizedBox(
width: 10, width: 10,
), ),
Expanded(child: Text(AppLocalizations.of(context)!.titleManageProfiles, style: TextStyle(color: settings.current().mainTextColor()))) Expanded(child: Text(MediaQuery.of(context).size.width > 600 ?
AppLocalizations.of(context)!.titleManageProfiles : AppLocalizations.of(context)!.titleManageProfilesShort,
style: TextStyle(color: settings.current().mainTextColor())))
]), ]),
actions: getActions(), actions: getActions(),
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
onPressed: _pushAddEditProfile, onPressed: _pushAddProfile,
tooltip: AppLocalizations.of(context)!.addNewProfileBtn, tooltip: AppLocalizations.of(context)!.addNewProfileBtn,
child: Icon( child: Icon(
Icons.add, Icons.add,
@ -95,6 +98,11 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
onPressed: _modalUnlockProfiles, onPressed: _modalUnlockProfiles,
)); ));
// Servers
if (Provider.of<Settings>(context).isExperimentEnabled(ServerManagementExperiment)) {
actions.add(IconButton(icon: Icon(CwtchIcons.dns_black_24dp), tooltip: "Servers", onPressed: _pushServers));
}
// Global Settings // Global Settings
actions.add(IconButton(icon: Icon(Icons.settings), tooltip: AppLocalizations.of(context)!.tooltipOpenSettings, onPressed: _pushGlobalSettings)); actions.add(IconButton(icon: Icon(Icons.settings), tooltip: AppLocalizations.of(context)!.tooltipOpenSettings, onPressed: _pushGlobalSettings));
@ -119,6 +127,17 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
)); ));
} }
void _pushServers() {
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (BuildContext context) {
return MultiProvider(
providers: [Provider.value(value: Provider.of<FlwtchState>(context))],
child: ServersView(),
);
},
));
}
void _pushTorStatus() { void _pushTorStatus() {
Navigator.of(context).push(MaterialPageRoute<void>( Navigator.of(context).push(MaterialPageRoute<void>(
builder: (BuildContext context) { builder: (BuildContext context) {
@ -130,7 +149,7 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
)); ));
} }
void _pushAddEditProfile({onion: ""}) { void _pushAddProfile({onion: ""}) {
Navigator.of(context).push(MaterialPageRoute<void>( Navigator.of(context).push(MaterialPageRoute<void>(
builder: (BuildContext context) { builder: (BuildContext context) {
return MultiProvider( return MultiProvider(

View File

@ -61,7 +61,7 @@ class _ProfileRowState extends State<ProfileRow> {
tooltip: AppLocalizations.of(context)!.editProfile + " " + profile.nickname, tooltip: AppLocalizations.of(context)!.editProfile + " " + profile.nickname,
icon: Icon(Icons.create, color: Provider.of<Settings>(context).current().mainTextColor()), icon: Icon(Icons.create, color: Provider.of<Settings>(context).current().mainTextColor()),
onPressed: () { onPressed: () {
_pushAddEditProfile(onion: profile.onion, displayName: profile.nickname, profileImage: profile.imagePath, encrypted: profile.isEncrypted); _pushEditProfile(onion: profile.onion, displayName: profile.nickname, profileImage: profile.imagePath, encrypted: profile.isEncrypted);
}, },
) )
], ],
@ -100,7 +100,7 @@ class _ProfileRowState extends State<ProfileRow> {
); );
} }
void _pushAddEditProfile({onion: "", displayName: "", profileImage: "", encrypted: true}) { void _pushEditProfile({onion: "", displayName: "", profileImage: "", encrypted: true}) {
Navigator.of(context).push(MaterialPageRoute<void>( Navigator.of(context).push(MaterialPageRoute<void>(
builder: (BuildContext context) { builder: (BuildContext context) {
return MultiProvider( return MultiProvider(