Formatting + Read/Write Settings
This commit is contained in:
parent
ce53fc68f2
commit
4190630cd9
|
@ -132,6 +132,10 @@ class MainActivity: FlutterActivity() {
|
||||||
val jsonEvent = (call.argument("jsonEvent") as? String) ?: "";
|
val jsonEvent = (call.argument("jsonEvent") as? String) ?: "";
|
||||||
Cwtch.sendProfileEvent(onion, jsonEvent);
|
Cwtch.sendProfileEvent(onion, jsonEvent);
|
||||||
}
|
}
|
||||||
|
"SendAppEvent" -> {
|
||||||
|
val jsonEvent = (call.argument("jsonEvent") as? String) ?: "";
|
||||||
|
Cwtch.sendAppEvent(jsonEvent);
|
||||||
|
}
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ abstract class Cwtch {
|
||||||
void CreateProfile(String nick, String pass);
|
void CreateProfile(String nick, String pass);
|
||||||
void LoadProfiles(String pass);
|
void LoadProfiles(String pass);
|
||||||
void SendProfileEvent(String onion, String jsonEvent);
|
void SendProfileEvent(String onion, String jsonEvent);
|
||||||
|
void SendAppEvent(String jsonEvent);
|
||||||
|
|
||||||
Future<String> ACNEvents();
|
Future<String> ACNEvents();
|
||||||
Future<String> ContactEvents();
|
Future<String> ContactEvents();
|
||||||
|
@ -15,4 +16,4 @@ abstract class Cwtch {
|
||||||
Future<int> NumMessages(String profile, String handle);
|
Future<int> NumMessages(String profile, String handle);
|
||||||
Future<String> GetMessage(String profile, String handle, int index);
|
Future<String> GetMessage(String profile, String handle, int index);
|
||||||
Future<String> GetMessages(String profile, String handle, int start, int end);
|
Future<String> GetMessages(String profile, String handle, int start, int end);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,32 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../model.dart';
|
import '../model.dart';
|
||||||
|
import '../settings.dart';
|
||||||
|
|
||||||
// Class that handles libcwtch-go events (received either via ffi with an isolate or gomobile over a method channel from kotlin)
|
// Class that handles libcwtch-go events (received either via ffi with an isolate or gomobile over a method channel from kotlin)
|
||||||
// Takes Notifiers and triggers them on appropriate events
|
// Takes Notifiers and triggers them on appropriate events
|
||||||
class CwtchNotifier {
|
class CwtchNotifier {
|
||||||
ProfileListState profileCN;
|
ProfileListState profileCN;
|
||||||
|
Settings settings;
|
||||||
|
|
||||||
CwtchNotifier(ProfileListState pcn) {
|
CwtchNotifier(ProfileListState pcn, Settings settingsCN) {
|
||||||
profileCN = pcn;
|
profileCN = pcn;
|
||||||
|
settings = settingsCN;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleMessage(String type, dynamic data) {
|
void handleMessage(String type, dynamic data) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "NewPeer":
|
case "NewPeer":
|
||||||
profileCN.add(ProfileInfoState(onion: data["Identity"], nickname: data["name"], imagePath: data["picture"]));
|
profileCN.add(ProfileInfoState(
|
||||||
|
onion: data["Identity"],
|
||||||
|
nickname: data["name"],
|
||||||
|
imagePath: data["picture"]));
|
||||||
|
break;
|
||||||
|
case "UpdateGlobalSettings":
|
||||||
|
settings.handleUpdate(jsonDecode(data["Data"]));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
print("unhandled gomobile appbus event: ${type}");
|
print("unhandled gomobile appbus event: ${type}");
|
||||||
|
|
|
@ -15,41 +15,55 @@ import '../model.dart';
|
||||||
/// Cwtch API ///
|
/// Cwtch API ///
|
||||||
/////////////////////
|
/////////////////////
|
||||||
|
|
||||||
typedef start_cwtch_function = Void Function(Pointer<Utf8> str, Int32 length, Pointer<Utf8> str2, Int32 length2);
|
typedef start_cwtch_function = Void Function(
|
||||||
typedef StartCwtchFn = void Function(Pointer<Utf8> dir, int len, Pointer<Utf8> tor, int torLen);
|
Pointer<Utf8> str, Int32 length, Pointer<Utf8> str2, Int32 length2);
|
||||||
|
typedef StartCwtchFn = void Function(
|
||||||
|
Pointer<Utf8> dir, int len, Pointer<Utf8> tor, int torLen);
|
||||||
|
|
||||||
typedef void_from_string_string_function = Void Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32);
|
typedef void_from_string_string_function = Void Function(
|
||||||
typedef VoidFromStringStringFn = void Function(Pointer<Utf8>, int, Pointer<Utf8>, int);
|
Pointer<Utf8>, Int32, Pointer<Utf8>, Int32);
|
||||||
|
typedef VoidFromStringStringFn = void Function(
|
||||||
|
Pointer<Utf8>, int, Pointer<Utf8>, int);
|
||||||
|
|
||||||
typedef access_cwtch_eventbus_function = Void Function();
|
typedef access_cwtch_eventbus_function = Void Function();
|
||||||
typedef NextEventFn = void Function();
|
typedef NextEventFn = void Function();
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
typedef string_string_to_void_function = Void Function(Pointer<Utf8> str, Int32 length, Pointer<Utf8> str2, Int32 length2);
|
typedef string_string_to_void_function = Void Function(
|
||||||
|
Pointer<Utf8> str, Int32 length, Pointer<Utf8> str2, Int32 length2);
|
||||||
typedef StringStringFn = void Function(Pointer<Utf8>, int, Pointer<Utf8>, int);
|
typedef StringStringFn = void Function(Pointer<Utf8>, int, Pointer<Utf8>, int);
|
||||||
|
|
||||||
typedef get_json_blob_void_function = Pointer<Utf8> Function();
|
typedef get_json_blob_void_function = Pointer<Utf8> Function();
|
||||||
typedef GetJsonBlobVoidFn = Pointer<Utf8> Function();
|
typedef GetJsonBlobVoidFn = Pointer<Utf8> Function();
|
||||||
|
|
||||||
typedef get_json_blob_string_function = Pointer<Utf8> Function(Pointer<Utf8> str, Int32 length);
|
typedef get_json_blob_string_function = Pointer<Utf8> Function(
|
||||||
typedef GetJsonBlobStringFn = Pointer<Utf8> Function(Pointer<Utf8> str,int len);
|
Pointer<Utf8> str, Int32 length);
|
||||||
|
typedef GetJsonBlobStringFn = Pointer<Utf8> Function(
|
||||||
|
Pointer<Utf8> str, int len);
|
||||||
|
|
||||||
//func NumMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int) (n C.int) {
|
//func NumMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int) (n C.int) {
|
||||||
typedef get_int_from_str_str_function = Int32 Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32);
|
typedef get_int_from_str_str_function = Int32 Function(
|
||||||
typedef GetIntFromStrStrFn = int Function(Pointer<Utf8>, int, Pointer<Utf8>, int);
|
Pointer<Utf8>, Int32, Pointer<Utf8>, Int32);
|
||||||
|
typedef GetIntFromStrStrFn = int Function(
|
||||||
|
Pointer<Utf8>, int, Pointer<Utf8>, int);
|
||||||
|
|
||||||
//func GetMessage(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, message_index C.int) *C.char {
|
//func GetMessage(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, message_index C.int) *C.char {
|
||||||
typedef get_json_blob_from_str_str_int_function = Pointer<Utf8> Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Int32);
|
typedef get_json_blob_from_str_str_int_function = Pointer<Utf8> Function(
|
||||||
typedef GetJsonBlobFromStrStrIntFn = Pointer<Utf8> Function(Pointer<Utf8>, int, Pointer<Utf8>, int, int);
|
Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Int32);
|
||||||
|
typedef GetJsonBlobFromStrStrIntFn = Pointer<Utf8> Function(
|
||||||
|
Pointer<Utf8>, int, Pointer<Utf8>, int, int);
|
||||||
|
|
||||||
//func GetMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, start C.int, end C.int) *C.char {
|
//func GetMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, start C.int, end C.int) *C.char {
|
||||||
typedef get_json_blob_from_str_str_int_int_function = Pointer<Utf8> Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Int32, Int32);
|
typedef get_json_blob_from_str_str_int_int_function = Pointer<Utf8> Function(
|
||||||
typedef GetJsonBlobFromStrStrIntIntFn = Pointer<Utf8> Function(Pointer<Utf8>, int, Pointer<Utf8>, int, int, int);
|
Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Int32, Int32);
|
||||||
|
typedef GetJsonBlobFromStrStrIntIntFn = Pointer<Utf8> Function(
|
||||||
|
Pointer<Utf8>, int, Pointer<Utf8>, int, int, int);
|
||||||
|
|
||||||
typedef acn_events_function = Pointer<Utf8> Function();
|
typedef acn_events_function = Pointer<Utf8> Function();
|
||||||
typedef ACNEventsFn = Pointer<Utf8> Function();
|
typedef ACNEventsFn = Pointer<Utf8> Function();
|
||||||
|
|
||||||
class CwtchFfi implements Cwtch {
|
class CwtchFfi implements Cwtch {
|
||||||
DynamicLibrary library;
|
DynamicLibrary library;
|
||||||
|
@ -72,17 +86,18 @@ class CwtchFfi implements Cwtch {
|
||||||
var cwtchDir = path.join(home, ".cwtch/dev/");
|
var cwtchDir = path.join(home, ".cwtch/dev/");
|
||||||
print("cwtchDir $cwtchDir");
|
print("cwtchDir $cwtchDir");
|
||||||
|
|
||||||
var startCwtchC = library.lookup<NativeFunction<start_cwtch_function>>("c_StartCwtch");
|
var startCwtchC =
|
||||||
|
library.lookup<NativeFunction<start_cwtch_function>>("c_StartCwtch");
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
final StartCwtch = startCwtchC.asFunction<StartCwtchFn>();
|
final StartCwtch = startCwtchC.asFunction<StartCwtchFn>();
|
||||||
|
|
||||||
|
|
||||||
final ut8CwtchDir = cwtchDir.toNativeUtf8();
|
final ut8CwtchDir = cwtchDir.toNativeUtf8();
|
||||||
StartCwtch(ut8CwtchDir,ut8CwtchDir.length, "".toNativeUtf8(), 0);
|
StartCwtch(ut8CwtchDir, ut8CwtchDir.length, "".toNativeUtf8(), 0);
|
||||||
|
|
||||||
// Spawn an isolate to listen to events from libcwtch-go and then dispatch them when received on main thread to cwtchNotifier
|
// Spawn an isolate to listen to events from libcwtch-go and then dispatch them when received on main thread to cwtchNotifier
|
||||||
var _receivePort = ReceivePort();
|
var _receivePort = ReceivePort();
|
||||||
cwtchIsolate = await Isolate.spawn(_checkAppbusEvents, _receivePort.sendPort);
|
cwtchIsolate =
|
||||||
|
await Isolate.spawn(_checkAppbusEvents, _receivePort.sendPort);
|
||||||
_receivePort.listen((message) {
|
_receivePort.listen((message) {
|
||||||
var env = jsonDecode(message);
|
var env = jsonDecode(message);
|
||||||
cwtchNotifier.handleMessage(env["EventType"], env["Data"]);
|
cwtchNotifier.handleMessage(env["EventType"], env["Data"]);
|
||||||
|
@ -108,7 +123,8 @@ class CwtchFfi implements Cwtch {
|
||||||
// Steam of appbus events. Call blocks in libcwtch-go GetAppbusEvent. Static so the isolate can use it
|
// Steam of appbus events. Call blocks in libcwtch-go GetAppbusEvent. Static so the isolate can use it
|
||||||
static Stream<String> pollAppbusEvents() async* {
|
static Stream<String> pollAppbusEvents() async* {
|
||||||
var library = DynamicLibrary.open("libCwtch.so");
|
var library = DynamicLibrary.open("libCwtch.so");
|
||||||
var getAppbusEventC = library.lookup<NativeFunction<acn_events_function>>("c_GetAppBusEvent");
|
var getAppbusEventC =
|
||||||
|
library.lookup<NativeFunction<acn_events_function>>("c_GetAppBusEvent");
|
||||||
final GetAppbusEvent = getAppbusEventC.asFunction<ACNEventsFn>();
|
final GetAppbusEvent = getAppbusEventC.asFunction<ACNEventsFn>();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -120,7 +136,9 @@ class CwtchFfi implements Cwtch {
|
||||||
|
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
void SelectProfile(String onion) async {
|
void SelectProfile(String onion) async {
|
||||||
var selectProfileC = library.lookup<NativeFunction<get_json_blob_string_function>>("c_SelectProfile");
|
var selectProfileC =
|
||||||
|
library.lookup<NativeFunction<get_json_blob_string_function>>(
|
||||||
|
"c_SelectProfile");
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
final SelectProfile = selectProfileC.asFunction<GetJsonBlobStringFn>();
|
final SelectProfile = selectProfileC.asFunction<GetJsonBlobStringFn>();
|
||||||
final ut8Onion = onion.toNativeUtf8();
|
final ut8Onion = onion.toNativeUtf8();
|
||||||
|
@ -129,7 +147,9 @@ class CwtchFfi implements Cwtch {
|
||||||
|
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
void CreateProfile(String nick, String pass) {
|
void CreateProfile(String nick, String pass) {
|
||||||
var createProfileC = library.lookup<NativeFunction<void_from_string_string_function>>("c_CreateProfile");
|
var createProfileC =
|
||||||
|
library.lookup<NativeFunction<void_from_string_string_function>>(
|
||||||
|
"c_CreateProfile");
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
final CreateProfile = createProfileC.asFunction<VoidFromStringStringFn>();
|
final CreateProfile = createProfileC.asFunction<VoidFromStringStringFn>();
|
||||||
final utf8nick = nick.toNativeUtf8();
|
final utf8nick = nick.toNativeUtf8();
|
||||||
|
@ -139,7 +159,8 @@ class CwtchFfi implements Cwtch {
|
||||||
|
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
void LoadProfiles(String pass) {
|
void LoadProfiles(String pass) {
|
||||||
var loadProfileC = library.lookup<NativeFunction<string_to_void_function>>("c_LoadProfiles");
|
var loadProfileC = library
|
||||||
|
.lookup<NativeFunction<string_to_void_function>>("c_LoadProfiles");
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
final LoadProfiles = loadProfileC.asFunction<StringFn>();
|
final LoadProfiles = loadProfileC.asFunction<StringFn>();
|
||||||
final ut8pass = pass.toNativeUtf8();
|
final ut8pass = pass.toNativeUtf8();
|
||||||
|
@ -147,8 +168,8 @@ class CwtchFfi implements Cwtch {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> ACNEvents() async {
|
Future<String> ACNEvents() async {
|
||||||
var acnEventsC = library.lookup<NativeFunction<acn_events_function>>(
|
var acnEventsC =
|
||||||
"c_ACNEvents");
|
library.lookup<NativeFunction<acn_events_function>>("c_ACNEvents");
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
final ACNEvents = acnEventsC.asFunction<ACNEventsFn>();
|
final ACNEvents = acnEventsC.asFunction<ACNEventsFn>();
|
||||||
|
|
||||||
|
@ -157,10 +178,9 @@ class CwtchFfi implements Cwtch {
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<String> ContactEvents() async {
|
Future<String> ContactEvents() async {
|
||||||
var acnEventsC = library.lookup<NativeFunction<acn_events_function>>(
|
var acnEventsC =
|
||||||
"c_ContactEvents");
|
library.lookup<NativeFunction<acn_events_function>>("c_ContactEvents");
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
final ContactEvents = acnEventsC.asFunction<ACNEventsFn>();
|
final ContactEvents = acnEventsC.asFunction<ACNEventsFn>();
|
||||||
|
|
||||||
|
@ -170,7 +190,8 @@ class CwtchFfi implements Cwtch {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> GetProfiles() async {
|
Future<String> GetProfiles() async {
|
||||||
var getProfilesC = library.lookup<NativeFunction<get_json_blob_void_function>>("c_GetProfiles");
|
var getProfilesC = library
|
||||||
|
.lookup<NativeFunction<get_json_blob_void_function>>("c_GetProfiles");
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
final GetProfiles = getProfilesC.asFunction<GetJsonBlobVoidFn>();
|
final GetProfiles = getProfilesC.asFunction<GetJsonBlobVoidFn>();
|
||||||
|
|
||||||
|
@ -180,7 +201,8 @@ class CwtchFfi implements Cwtch {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> GetContacts(String onion) async {
|
Future<String> GetContacts(String onion) async {
|
||||||
var getContactsC = library.lookup<NativeFunction<get_json_blob_string_function>>("c_GetContacts");
|
var getContactsC = library
|
||||||
|
.lookup<NativeFunction<get_json_blob_string_function>>("c_GetContacts");
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
final GetContacts = getContactsC.asFunction<GetJsonBlobStringFn>();
|
final GetContacts = getContactsC.asFunction<GetJsonBlobStringFn>();
|
||||||
final utf8onion = onion.toNativeUtf8();
|
final utf8onion = onion.toNativeUtf8();
|
||||||
|
@ -190,44 +212,66 @@ class CwtchFfi implements Cwtch {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> NumMessages(String profile, String handle) async {
|
Future<int> NumMessages(String profile, String handle) async {
|
||||||
var numMessagesC = library.lookup<NativeFunction<get_int_from_str_str_function>>("c_NumMessages");
|
var numMessagesC = library
|
||||||
|
.lookup<NativeFunction<get_int_from_str_str_function>>("c_NumMessages");
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
final NumMessages = numMessagesC.asFunction<GetIntFromStrStrFn>();
|
final NumMessages = numMessagesC.asFunction<GetIntFromStrStrFn>();
|
||||||
final utf8profile = profile.toNativeUtf8();
|
final utf8profile = profile.toNativeUtf8();
|
||||||
final utf8handle = handle.toNativeUtf8();
|
final utf8handle = handle.toNativeUtf8();
|
||||||
int num = NumMessages(utf8profile, utf8profile.length, utf8handle, utf8handle.length);
|
int num = NumMessages(
|
||||||
|
utf8profile, utf8profile.length, utf8handle, utf8handle.length);
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> GetMessage(String profile, String handle, int index) async {
|
Future<String> GetMessage(String profile, String handle, int index) async {
|
||||||
var getMessageC = library.lookup<NativeFunction<get_json_blob_from_str_str_int_function>>("c_GetMessage");
|
var getMessageC =
|
||||||
|
library.lookup<NativeFunction<get_json_blob_from_str_str_int_function>>(
|
||||||
|
"c_GetMessage");
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
final GetMessage = getMessageC.asFunction<GetJsonBlobFromStrStrIntFn>();
|
final GetMessage = getMessageC.asFunction<GetJsonBlobFromStrStrIntFn>();
|
||||||
final utf8profile = profile.toNativeUtf8();
|
final utf8profile = profile.toNativeUtf8();
|
||||||
final utf8handle = handle.toNativeUtf8();
|
final utf8handle = handle.toNativeUtf8();
|
||||||
Pointer<Utf8> jsonMessageBytes = GetMessage(utf8profile, utf8profile.length, utf8handle, utf8handle.length, index);
|
Pointer<Utf8> jsonMessageBytes = GetMessage(
|
||||||
|
utf8profile, utf8profile.length, utf8handle, utf8handle.length, index);
|
||||||
String jsonMessage = jsonMessageBytes.toDartString();
|
String jsonMessage = jsonMessageBytes.toDartString();
|
||||||
return jsonMessage;
|
return jsonMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> GetMessages(String profile, String handle, int start, int end) async {
|
Future<String> GetMessages(
|
||||||
var getMessagesC = library.lookup<NativeFunction<get_json_blob_from_str_str_int_int_function>>("c_GetMessages");
|
String profile, String handle, int start, int end) async {
|
||||||
|
var getMessagesC = library
|
||||||
|
.lookup<NativeFunction<get_json_blob_from_str_str_int_int_function>>(
|
||||||
|
"c_GetMessages");
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
final GetMessages = getMessagesC.asFunction<GetJsonBlobFromStrStrIntIntFn>();
|
final GetMessages =
|
||||||
|
getMessagesC.asFunction<GetJsonBlobFromStrStrIntIntFn>();
|
||||||
final utf8profile = profile.toNativeUtf8();
|
final utf8profile = profile.toNativeUtf8();
|
||||||
final utf8handle = handle.toNativeUtf8();
|
final utf8handle = handle.toNativeUtf8();
|
||||||
Pointer<Utf8> jsonMessagesBytes = GetMessages(utf8profile, utf8profile.length, utf8handle, utf8handle.length, start, end);
|
Pointer<Utf8> jsonMessagesBytes = GetMessages(utf8profile,
|
||||||
|
utf8profile.length, utf8handle, utf8handle.length, start, end);
|
||||||
String jsonMessages = jsonMessagesBytes.toDartString();
|
String jsonMessages = jsonMessagesBytes.toDartString();
|
||||||
return jsonMessages;
|
return jsonMessages;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void SendProfileEvent(String onion, String json) {
|
void SendProfileEvent(String onion, String json) {
|
||||||
var sendAppBusEvent = library.lookup<NativeFunction<string_string_to_void_function>>("c_SendProfileEvent");
|
var sendAppBusEvent =
|
||||||
|
library.lookup<NativeFunction<string_string_to_void_function>>(
|
||||||
|
"c_SendProfileEvent");
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
final SendAppBusEvent = sendAppBusEvent.asFunction<StringStringFn>();
|
final SendAppBusEvent = sendAppBusEvent.asFunction<StringStringFn>();
|
||||||
final utf8onion = onion.toNativeUtf8();
|
final utf8onion = onion.toNativeUtf8();
|
||||||
final utf8json = json.toNativeUtf8();
|
final utf8json = json.toNativeUtf8();
|
||||||
SendAppBusEvent(utf8onion, utf8onion.length, utf8json, utf8json.length);
|
SendAppBusEvent(utf8onion, utf8onion.length, utf8json, utf8json.length);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@override
|
||||||
|
void SendAppEvent(String json) {
|
||||||
|
var sendAppBusEvent = library
|
||||||
|
.lookup<NativeFunction<string_to_void_function>>("c_SendAppEvent");
|
||||||
|
// ignore: non_constant_identifier_names
|
||||||
|
final SendAppBusEvent = sendAppBusEvent.asFunction<StringFn>();
|
||||||
|
final utf8json = json.toNativeUtf8();
|
||||||
|
SendAppBusEvent(utf8json, utf8json.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,7 +22,8 @@ Future startCwtch() async {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class CwtchGomobile implements Cwtch {
|
class CwtchGomobile implements Cwtch {
|
||||||
static const appInfoPlatform = const MethodChannel('test.flutter.dev/applicationInfo');
|
static const appInfoPlatform =
|
||||||
|
const MethodChannel('test.flutter.dev/applicationInfo');
|
||||||
static const cwtchPlatform = const MethodChannel('cwtch');
|
static const cwtchPlatform = const MethodChannel('cwtch');
|
||||||
|
|
||||||
final appbusEventChannelName = 'test.flutter.dev/eventBus';
|
final appbusEventChannelName = 'test.flutter.dev/eventBus';
|
||||||
|
@ -47,7 +48,8 @@ class CwtchGomobile implements Cwtch {
|
||||||
var cwtchDir = path.join((await androidHomeDirectory).path, ".cwtch/dev/");
|
var cwtchDir = path.join((await androidHomeDirectory).path, ".cwtch/dev/");
|
||||||
String torPath = path.join(await androidLibraryDir, "libtor.so");
|
String torPath = path.join(await androidLibraryDir, "libtor.so");
|
||||||
print("gomobile.dart: Start invokeMethod Start($cwtchDir, $torPath)...");
|
print("gomobile.dart: Start invokeMethod Start($cwtchDir, $torPath)...");
|
||||||
cwtchPlatform.invokeMethod("Start", {"appDir": cwtchDir, "torPath": torPath});
|
cwtchPlatform
|
||||||
|
.invokeMethod("Start", {"appDir": cwtchDir, "torPath": torPath});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle libcwtch-go events (received via kotlin) and dispatch to the cwtchNotifier
|
// Handle libcwtch-go events (received via kotlin) and dispatch to the cwtchNotifier
|
||||||
|
@ -59,7 +61,7 @@ class CwtchGomobile implements Cwtch {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelectProfile(String onion) {
|
void SelectProfile(String onion) {
|
||||||
cwtchPlatform.invokeMethod("SelectProfile", {"profile" : onion});
|
cwtchPlatform.invokeMethod("SelectProfile", {"profile": onion});
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateProfile(String nick, String pass) {
|
void CreateProfile(String nick, String pass) {
|
||||||
|
@ -84,26 +86,34 @@ class CwtchGomobile implements Cwtch {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> GetContacts(String onion) {
|
Future<String> GetContacts(String onion) {
|
||||||
return cwtchPlatform.invokeMethod("GetContacts", {"profile" : onion});
|
return cwtchPlatform.invokeMethod("GetContacts", {"profile": onion});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> NumMessages(String profile, String handle) {
|
Future<int> NumMessages(String profile, String handle) {
|
||||||
return cwtchPlatform.invokeMethod("NumMessages", {"profile" : profile, "contact": handle});
|
return cwtchPlatform
|
||||||
|
.invokeMethod("NumMessages", {"profile": profile, "contact": handle});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> GetMessage(String profile, String handle, int index) {
|
Future<String> GetMessage(String profile, String handle, int index) {
|
||||||
print("gomobile.dart GetMessage " + index.toString());
|
print("gomobile.dart GetMessage " + index.toString());
|
||||||
return cwtchPlatform.invokeMethod("GetMessage", {"profile" : profile, "contact": handle, "index": index});
|
return cwtchPlatform.invokeMethod(
|
||||||
|
"GetMessage", {"profile": profile, "contact": handle, "index": index});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> GetMessages(String profile, String handle, int start, int end) {
|
Future<String> GetMessages(
|
||||||
return cwtchPlatform.invokeMethod("GetMessage", {"profile" : profile, "contact": handle, "start": start, "end": end});
|
String profile, String handle, int start, int end) {
|
||||||
|
return cwtchPlatform.invokeMethod("GetMessage",
|
||||||
|
{"profile": profile, "contact": handle, "start": start, "end": end});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void SendProfileEvent(String onion, String jsonEvent) {
|
void SendProfileEvent(String onion, String jsonEvent) {
|
||||||
cwtchPlatform.invokeMethod("SendProfileEvent", {"onion" : onion, "jsonEvent": jsonEvent});
|
cwtchPlatform.invokeMethod(
|
||||||
|
"SendProfileEvent", {"onion": onion, "jsonEvent": jsonEvent});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
}
|
void SendAppEvent(String jsonEvent) {
|
||||||
|
cwtchPlatform.invokeMethod("SendAppEvent", {"jsonEvent": jsonEvent});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@ import 'dart:io' show Platform;
|
||||||
import 'opaque.dart';
|
import 'opaque.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
var GlobalSettings = Settings(Locale("en", ''), Opaque.dark);
|
||||||
|
|
||||||
void main() => runApp(Flwtch());
|
void main() => runApp(Flwtch());
|
||||||
|
|
||||||
class Flwtch extends StatefulWidget {
|
class Flwtch extends StatefulWidget {
|
||||||
|
@ -39,7 +41,7 @@ class FlwtchState extends State<Flwtch> {
|
||||||
cwtchInit = false;
|
cwtchInit = false;
|
||||||
|
|
||||||
profs = ProfileListState();
|
profs = ProfileListState();
|
||||||
var cwtchNotifier = new CwtchNotifier(profs);
|
var cwtchNotifier = new CwtchNotifier(profs, GlobalSettings);
|
||||||
|
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
cwtch = CwtchGomobile(cwtchNotifier);
|
cwtch = CwtchGomobile(cwtchNotifier);
|
||||||
|
@ -56,20 +58,26 @@ class FlwtchState extends State<Flwtch> {
|
||||||
appStatus = AppModel(cwtch: cwtch);
|
appStatus = AppModel(cwtch: cwtch);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChangeNotifierProvider<Settings> getSettingsProvider() => ChangeNotifierProvider(create: (context) => Settings(Locale("en", '')));
|
ChangeNotifierProvider<Settings> getSettingsProvider() =>
|
||||||
ChangeNotifierProvider<OpaqueTheme> getOpaqueProvider() => ChangeNotifierProvider(create: (context) => OpaqueTheme(Opaque.dark));
|
ChangeNotifierProvider(create: (context) => GlobalSettings);
|
||||||
Provider<FlwtchState> getFlwtchStateProvider() => Provider<FlwtchState>(create: (_) => this);
|
Provider<FlwtchState> getFlwtchStateProvider() =>
|
||||||
ChangeNotifierProvider<ProfileListState> getProfileListProvider() => ChangeNotifierProvider(create: (context) => profs);
|
Provider<FlwtchState>(create: (_) => this);
|
||||||
|
ChangeNotifierProvider<ProfileListState> getProfileListProvider() =>
|
||||||
|
ChangeNotifierProvider(create: (context) => profs);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
appStatus = AppModel(cwtch: cwtch);
|
appStatus = AppModel(cwtch: cwtch);
|
||||||
|
|
||||||
return MultiProvider(
|
return MultiProvider(
|
||||||
providers: [getFlwtchStateProvider(), getProfileListProvider(), getOpaqueProvider(), getSettingsProvider()],
|
providers: [
|
||||||
|
getFlwtchStateProvider(),
|
||||||
|
getProfileListProvider(),
|
||||||
|
getSettingsProvider()
|
||||||
|
],
|
||||||
builder: (context, widget) {
|
builder: (context, widget) {
|
||||||
Provider.of<Settings>(context).initPackageInfo();
|
Provider.of<Settings>(context).initPackageInfo();
|
||||||
return Consumer<OpaqueTheme>(
|
return Consumer<Settings>(
|
||||||
builder: (context, opaque, child) => MaterialApp(
|
builder: (context, opaque, child) => MaterialApp(
|
||||||
locale: Provider.of<Settings>(context).locale,
|
locale: Provider.of<Settings>(context).locale,
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
|
@ -83,42 +91,46 @@ class FlwtchState extends State<Flwtch> {
|
||||||
accentColor: opaque.current().defaultButtonColor(),
|
accentColor: opaque.current().defaultButtonColor(),
|
||||||
buttonColor: opaque.current().defaultButtonColor(),
|
buttonColor: opaque.current().defaultButtonColor(),
|
||||||
backgroundColor: opaque.current().backgroundMainColor(),
|
backgroundColor: opaque.current().backgroundMainColor(),
|
||||||
iconTheme: IconThemeData (
|
iconTheme: IconThemeData(
|
||||||
color: opaque.current().mainTextColor(),
|
color: opaque.current().mainTextColor(),
|
||||||
),
|
),
|
||||||
cardColor: opaque.current().backgroundMainColor(),
|
cardColor: opaque.current().backgroundMainColor(),
|
||||||
textButtonTheme: TextButtonThemeData (
|
textButtonTheme: TextButtonThemeData(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor: MaterialStateProperty.all(opaque.current().defaultButtonColor()),
|
backgroundColor: MaterialStateProperty.all(
|
||||||
foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor()),
|
opaque.current().defaultButtonColor()),
|
||||||
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor()),
|
foregroundColor: MaterialStateProperty.all(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.all(20))
|
opaque.current().defaultButtonTextColor()),
|
||||||
),
|
overlayColor: MaterialStateProperty.all(
|
||||||
),
|
opaque.current().defaultButtonActiveColor()),
|
||||||
dialogTheme: DialogTheme (
|
padding: MaterialStateProperty.all(EdgeInsets.all(20))),
|
||||||
backgroundColor: opaque.current().backgroundPaneColor(),
|
|
||||||
titleTextStyle: TextStyle( color: opaque.current().mainTextColor()),
|
|
||||||
contentTextStyle: TextStyle( color: opaque.current().mainTextColor())
|
|
||||||
),
|
),
|
||||||
|
dialogTheme: DialogTheme(
|
||||||
|
backgroundColor: opaque.current().backgroundPaneColor(),
|
||||||
|
titleTextStyle:
|
||||||
|
TextStyle(color: opaque.current().mainTextColor()),
|
||||||
|
contentTextStyle:
|
||||||
|
TextStyle(color: opaque.current().mainTextColor())),
|
||||||
textTheme: TextTheme(
|
textTheme: TextTheme(
|
||||||
headline1: TextStyle( color: opaque.current().mainTextColor()),
|
headline1: TextStyle(color: opaque.current().mainTextColor()),
|
||||||
headline2: TextStyle( color: opaque.current().mainTextColor()),
|
headline2: TextStyle(color: opaque.current().mainTextColor()),
|
||||||
headline3: TextStyle( color: opaque.current().mainTextColor()),
|
headline3: TextStyle(color: opaque.current().mainTextColor()),
|
||||||
headline4: TextStyle( color: opaque.current().mainTextColor()),
|
headline4: TextStyle(color: opaque.current().mainTextColor()),
|
||||||
headline5: TextStyle( color: opaque.current().mainTextColor()),
|
headline5: TextStyle(color: opaque.current().mainTextColor()),
|
||||||
headline6: TextStyle( color: opaque.current().mainTextColor()),
|
headline6: TextStyle(color: opaque.current().mainTextColor()),
|
||||||
bodyText1: TextStyle( color: opaque.current().mainTextColor()),
|
bodyText1: TextStyle(color: opaque.current().mainTextColor()),
|
||||||
bodyText2: TextStyle( color: opaque.current().mainTextColor()),
|
bodyText2: TextStyle(color: opaque.current().mainTextColor()),
|
||||||
subtitle1: TextStyle( color: opaque.current().mainTextColor()),
|
subtitle1: TextStyle(color: opaque.current().mainTextColor()),
|
||||||
subtitle2: TextStyle( color: opaque.current().mainTextColor()),
|
subtitle2: TextStyle(color: opaque.current().mainTextColor()),
|
||||||
caption: TextStyle( color: opaque.current().mainTextColor()),
|
caption: TextStyle(color: opaque.current().mainTextColor()),
|
||||||
button: TextStyle( color: opaque.current().mainTextColor()),
|
button: TextStyle(color: opaque.current().mainTextColor()),
|
||||||
overline: TextStyle( color: opaque.current().mainTextColor())
|
overline: TextStyle(color: opaque.current().mainTextColor())),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
// from dan: home: cwtchInit == true ? ProfileMgrView(cwtch) : SplashView(),
|
// from dan: home: cwtchInit == true ? ProfileMgrView(cwtch) : SplashView(),
|
||||||
// from erinn: home: columns.length == 3 ? TripleColumnView() : ProfileMgrView(),
|
// from erinn: home: columns.length == 3 ? TripleColumnView() : ProfileMgrView(),
|
||||||
home: cwtchInit == true ? (columns.length == 3 ? TripleColumnView() : ProfileMgrView()) : SplashView(),
|
home: cwtchInit == true
|
||||||
|
? (columns.length == 3 ? TripleColumnView() : ProfileMgrView())
|
||||||
|
: SplashView(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -29,7 +29,13 @@ class ContactModel {
|
||||||
String status;
|
String status;
|
||||||
String imagePath;
|
String imagePath;
|
||||||
|
|
||||||
ContactModel({this.onion, this.nickname, this.status, this.isInvitation, this.isBlocked, this.imagePath});
|
ContactModel(
|
||||||
|
{this.onion,
|
||||||
|
this.nickname,
|
||||||
|
this.status,
|
||||||
|
this.isInvitation,
|
||||||
|
this.isBlocked,
|
||||||
|
this.imagePath});
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo: delete
|
//todo: delete
|
||||||
|
@ -52,8 +58,7 @@ class ChatMessage {
|
||||||
: o = json['o'],
|
: o = json['o'],
|
||||||
d = json['d'];
|
d = json['d'];
|
||||||
|
|
||||||
Map<String, dynamic> toJson() =>
|
Map<String, dynamic> toJson() => {
|
||||||
{
|
|
||||||
'o': o,
|
'o': o,
|
||||||
'd': d,
|
'd': d,
|
||||||
};
|
};
|
||||||
|
@ -73,12 +78,13 @@ class ProfileListState extends ChangeNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(ProfileInfoState newOnion) {
|
void add(ProfileInfoState newOnion) {
|
||||||
print("ProfileListState: adding " + newOnion.onion +" and notifying");
|
print("ProfileListState: adding " + newOnion.onion + " and notifying");
|
||||||
_onions.add(newOnion);
|
_onions.add(newOnion);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ProfileInfoState> get onions => _onions.sublist(0);//todo: copy?? dont want caller able to bypass changenotifier
|
List<ProfileInfoState> get onions => _onions
|
||||||
|
.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier
|
||||||
}
|
}
|
||||||
|
|
||||||
class ContactListState extends ChangeNotifier {
|
class ContactListState extends ChangeNotifier {
|
||||||
|
@ -117,13 +123,16 @@ class ContactListState extends ChangeNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateUnreadMessages(String forOnion, int newVal) {
|
void updateUnreadMessages(String forOnion, int newVal) {
|
||||||
_onions.sort((ContactInfoState a, ContactInfoState b) { return b.unreadMessages - a.unreadMessages; });
|
_onions.sort((ContactInfoState a, ContactInfoState b) {
|
||||||
|
return b.unreadMessages - a.unreadMessages;
|
||||||
|
});
|
||||||
//<todo> if(changed) {
|
//<todo> if(changed) {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
//} </todo>
|
//} </todo>
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ContactInfoState> get onions => _onions.sublist(0);//todo: copy?? dont want caller able to bypass changenotifier
|
List<ContactInfoState> get onions => _onions
|
||||||
|
.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProfileInfoState extends ChangeNotifier {
|
class ProfileInfoState extends ChangeNotifier {
|
||||||
|
@ -132,7 +141,12 @@ class ProfileInfoState extends ChangeNotifier {
|
||||||
String _imagePath = "";
|
String _imagePath = "";
|
||||||
int _unreadMessages = 0;
|
int _unreadMessages = 0;
|
||||||
|
|
||||||
ProfileInfoState({this.onion, nickname = "", imagePath = "", unreadMessages = 0,}){
|
ProfileInfoState({
|
||||||
|
this.onion,
|
||||||
|
nickname = "",
|
||||||
|
imagePath = "",
|
||||||
|
unreadMessages = 0,
|
||||||
|
}) {
|
||||||
this._nickname = nickname;
|
this._nickname = nickname;
|
||||||
this._imagePath = imagePath;
|
this._imagePath = imagePath;
|
||||||
this._unreadMessages = unreadMessages;
|
this._unreadMessages = unreadMessages;
|
||||||
|
@ -174,7 +188,16 @@ class ContactInfoState extends ChangeNotifier {
|
||||||
String _imagePath;
|
String _imagePath;
|
||||||
int _unreadMessages = 0;
|
int _unreadMessages = 0;
|
||||||
|
|
||||||
ContactInfoState({this.profileOnion, this.onion, nickname = "", isGroup = false, isInvitation = false, isBlocked = false, status = "", imagePath = "",}) {
|
ContactInfoState({
|
||||||
|
this.profileOnion,
|
||||||
|
this.onion,
|
||||||
|
nickname = "",
|
||||||
|
isGroup = false,
|
||||||
|
isInvitation = false,
|
||||||
|
isBlocked = false,
|
||||||
|
status = "",
|
||||||
|
imagePath = "",
|
||||||
|
}) {
|
||||||
this._nickname = nickname;
|
this._nickname = nickname;
|
||||||
this._isGroup = isGroup;
|
this._isGroup = isGroup;
|
||||||
this._isInvitation = isInvitation;
|
this._isInvitation = isInvitation;
|
||||||
|
@ -212,8 +235,6 @@ class ContactInfoState extends ChangeNotifier {
|
||||||
/// ACN ///
|
/// ACN ///
|
||||||
/////////////
|
/////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AppModel {
|
class AppModel {
|
||||||
final Cwtch cwtch;
|
final Cwtch cwtch;
|
||||||
AppModel({this.cwtch});
|
AppModel({this.cwtch});
|
||||||
|
|
1655
lib/opaque.dart
1655
lib/opaque.dart
File diff suppressed because it is too large
Load Diff
|
@ -4,11 +4,40 @@ import 'dart:core';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
|
||||||
|
import 'opaque.dart';
|
||||||
|
|
||||||
class Settings extends ChangeNotifier {
|
class Settings extends ChangeNotifier {
|
||||||
Locale locale;
|
Locale locale;
|
||||||
|
|
||||||
PackageInfo packageInfo;
|
PackageInfo packageInfo;
|
||||||
|
|
||||||
|
OpaqueThemeType theme;
|
||||||
|
|
||||||
|
void setDark() {
|
||||||
|
theme = Opaque.dark;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLight() {
|
||||||
|
theme = Opaque.light;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
OpaqueThemeType current() {
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUpdate(dynamic settings) {
|
||||||
|
print("Settings ${settings}");
|
||||||
|
switchLocale(Locale(settings["Locale"]));
|
||||||
|
if (settings["Theme"] == "light") {
|
||||||
|
this.setLight();
|
||||||
|
} else {
|
||||||
|
this.setDark();
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
initPackageInfo() {
|
initPackageInfo() {
|
||||||
PackageInfo.fromPlatform().then((PackageInfo newPackageInfo) {
|
PackageInfo.fromPlatform().then((PackageInfo newPackageInfo) {
|
||||||
packageInfo = newPackageInfo;
|
packageInfo = newPackageInfo;
|
||||||
|
@ -21,5 +50,22 @@ class Settings extends ChangeNotifier {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings(this.locale);
|
Settings(this.locale, this.theme);
|
||||||
}
|
|
||||||
|
dynamic asJson() {
|
||||||
|
var themeString = "light";
|
||||||
|
if (theme == Opaque.dark) {
|
||||||
|
themeString = "dark";
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"Locale": this.locale.languageCode,
|
||||||
|
"Theme": themeString,
|
||||||
|
"PreviousPid": -1,
|
||||||
|
"ExperimentsEnabled": false,
|
||||||
|
"Experiments": {},
|
||||||
|
"StateRootPane": 0,
|
||||||
|
"FirstTime": false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,13 +18,14 @@ class _AddContactViewState extends State<AddContactView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildForm() {
|
Widget _buildForm() {
|
||||||
return Center(child:Wrap(
|
return Center(
|
||||||
|
child: Wrap(
|
||||||
direction: Axis.vertical,
|
direction: Axis.vertical,
|
||||||
spacing: 20.0,
|
spacing: 20.0,
|
||||||
runSpacing: 20.0,
|
runSpacing: 20.0,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(AppLocalizations.of(context).profileName),
|
Text(AppLocalizations.of(context).profileName),
|
||||||
Text("peer handle or group invite or server bundle"),//todo
|
Text("peer handle or group invite or server bundle"), //todo
|
||||||
Text(AppLocalizations.of(context).createGroupBtn),
|
Text(AppLocalizations.of(context).createGroupBtn),
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
|
|
@ -12,6 +12,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
import '../main.dart';
|
import '../main.dart';
|
||||||
import '../opaque.dart';
|
import '../opaque.dart';
|
||||||
|
import '../settings.dart';
|
||||||
|
|
||||||
class AddEditProfileView extends StatefulWidget {
|
class AddEditProfileView extends StatefulWidget {
|
||||||
const AddEditProfileView({Key key}) : super(key: key);
|
const AddEditProfileView({Key key}) : super(key: key);
|
||||||
|
@ -46,7 +47,9 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
||||||
ctrlrOnion.text = Provider.of<ProfileInfoState>(context).onion;
|
ctrlrOnion.text = Provider.of<ProfileInfoState>(context).onion;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(Provider.of<ProfileInfoState>(context).onion.isEmpty ? AppLocalizations.of(context).addProfileTitle : AppLocalizations.of(context).editProfileTitle),
|
title: Text(Provider.of<ProfileInfoState>(context).onion.isEmpty
|
||||||
|
? AppLocalizations.of(context).addProfileTitle
|
||||||
|
: AppLocalizations.of(context).editProfileTitle),
|
||||||
),
|
),
|
||||||
body: _buildForm(),
|
body: _buildForm(),
|
||||||
);
|
);
|
||||||
|
@ -62,8 +65,9 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
||||||
// We use Visibility to hide optional structures when they are not requested.
|
// We use Visibility to hide optional structures when they are not requested.
|
||||||
// We used SizedBox for inter-widget height padding in columns, otherwise elements can render a little too close together.
|
// We used SizedBox for inter-widget height padding in columns, otherwise elements can render a little too close together.
|
||||||
Widget _buildForm() {
|
Widget _buildForm() {
|
||||||
return Consumer<OpaqueTheme>(builder: (context, theme, child) {
|
return Consumer<Settings>(builder: (context, theme, child) {
|
||||||
return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
return LayoutBuilder(
|
||||||
|
builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||||
return Scrollbar(
|
return Scrollbar(
|
||||||
isAlwaysShown: true,
|
isAlwaysShown: true,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
|
@ -77,180 +81,306 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: EdgeInsets.all(30),
|
margin: EdgeInsets.all(30),
|
||||||
padding: EdgeInsets.all(20),
|
padding: EdgeInsets.all(20),
|
||||||
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: [
|
child: Column(
|
||||||
Visibility(
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
visible: Provider.of<ProfileInfoState>(context).onion.isNotEmpty,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
|
children: [
|
||||||
SizedBox(
|
|
||||||
width: 120,
|
|
||||||
height: 120,
|
|
||||||
child: ClipOval(
|
|
||||||
child: SizedBox(
|
|
||||||
width: 120,
|
|
||||||
height: 120,
|
|
||||||
child: Container(
|
|
||||||
color: Colors.white,
|
|
||||||
width: 120,
|
|
||||||
height: 120,
|
|
||||||
child: Image(
|
|
||||||
image: AssetImage("assets/" + Provider.of<ProfileInfoState>(context).imagePath),
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
))),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
])),
|
|
||||||
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
|
||||||
CwtchLabel(label: AppLocalizations.of(context).displayNameLabel),
|
|
||||||
SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
CwtchTextField(
|
|
||||||
controller: ctrlrNick,
|
|
||||||
labelText: AppLocalizations.of(context).yourDisplayName,
|
|
||||||
validator: (value) {
|
|
||||||
if (value.isEmpty) {
|
|
||||||
return "Please enter a display name";
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
Visibility(
|
|
||||||
visible: Provider.of<ProfileInfoState>(context).onion.isNotEmpty,
|
|
||||||
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [
|
|
||||||
SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
CwtchLabel(label: AppLocalizations.of(context).addressLabel),
|
|
||||||
SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
CwtchButtonTextField(
|
|
||||||
controller: ctrlrOnion,
|
|
||||||
onPressed: _copyOnion,
|
|
||||||
icon: Icon(Icons.copy),
|
|
||||||
tooltip: AppLocalizations.of(context).copyBtn,
|
|
||||||
)
|
|
||||||
])),
|
|
||||||
// We only allow setting password types on profile creation
|
|
||||||
Visibility(
|
|
||||||
visible: Provider.of<ProfileInfoState>(context).onion.isEmpty,
|
|
||||||
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
|
|
||||||
Radio(
|
|
||||||
value: false,
|
|
||||||
groupValue: usePassword,
|
|
||||||
onChanged: _handleSwitchPassword,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context).radioNoPassword,
|
|
||||||
style: TextStyle(color: theme.current().mainTextColor()),
|
|
||||||
),
|
|
||||||
Radio(
|
|
||||||
value: true,
|
|
||||||
groupValue: usePassword,
|
|
||||||
onChanged: _handleSwitchPassword,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context).radioUsePassword,
|
|
||||||
style: TextStyle(color: theme.current().mainTextColor()),
|
|
||||||
),
|
|
||||||
])),
|
|
||||||
SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
Visibility(
|
|
||||||
visible: usePassword,
|
|
||||||
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
|
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: Provider.of<ProfileInfoState>(context, listen: false).onion.isNotEmpty,
|
visible:
|
||||||
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [
|
Provider.of<ProfileInfoState>(context)
|
||||||
CwtchLabel(label: AppLocalizations.of(context).currentPasswordLabel),
|
.onion
|
||||||
|
.isNotEmpty,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
SizedBox(
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
child: ClipOval(
|
||||||
|
child: SizedBox(
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
child: Container(
|
||||||
|
color: Colors.white,
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
child: Image(
|
||||||
|
image: AssetImage("assets/" +
|
||||||
|
Provider.of<ProfileInfoState>(
|
||||||
|
context)
|
||||||
|
.imagePath),
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
))),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
])),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
CwtchLabel(
|
||||||
|
label: AppLocalizations.of(context)
|
||||||
|
.displayNameLabel),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
CwtchPasswordField(
|
CwtchTextField(
|
||||||
controller: ctrlrOldPass,
|
controller: ctrlrNick,
|
||||||
|
labelText:
|
||||||
|
AppLocalizations.of(context)
|
||||||
|
.yourDisplayName,
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
// Password field can be empty when just updating the profile, not on creation
|
if (value.isEmpty) {
|
||||||
if (Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty && value.isEmpty && usePassword) {
|
return "Please enter a display name";
|
||||||
return AppLocalizations.of(context).passwordErrorEmpty;
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(
|
]),
|
||||||
height: 20,
|
Visibility(
|
||||||
),
|
visible:
|
||||||
])),
|
Provider.of<ProfileInfoState>(context)
|
||||||
CwtchLabel(label: AppLocalizations.of(context).password1Label),
|
.onion
|
||||||
|
.isNotEmpty,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
CwtchLabel(
|
||||||
|
label:
|
||||||
|
AppLocalizations.of(context)
|
||||||
|
.addressLabel),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
CwtchButtonTextField(
|
||||||
|
controller: ctrlrOnion,
|
||||||
|
onPressed: _copyOnion,
|
||||||
|
icon: Icon(Icons.copy),
|
||||||
|
tooltip:
|
||||||
|
AppLocalizations.of(context)
|
||||||
|
.copyBtn,
|
||||||
|
)
|
||||||
|
])),
|
||||||
|
// We only allow setting password types on profile creation
|
||||||
|
Visibility(
|
||||||
|
visible:
|
||||||
|
Provider.of<ProfileInfoState>(context)
|
||||||
|
.onion
|
||||||
|
.isEmpty,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Radio(
|
||||||
|
value: false,
|
||||||
|
groupValue: usePassword,
|
||||||
|
onChanged: _handleSwitchPassword,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)
|
||||||
|
.radioNoPassword,
|
||||||
|
style: TextStyle(
|
||||||
|
color: theme
|
||||||
|
.current()
|
||||||
|
.mainTextColor()),
|
||||||
|
),
|
||||||
|
Radio(
|
||||||
|
value: true,
|
||||||
|
groupValue: usePassword,
|
||||||
|
onChanged: _handleSwitchPassword,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)
|
||||||
|
.radioUsePassword,
|
||||||
|
style: TextStyle(
|
||||||
|
color: theme
|
||||||
|
.current()
|
||||||
|
.mainTextColor()),
|
||||||
|
),
|
||||||
|
])),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
CwtchPasswordField(
|
Visibility(
|
||||||
controller: ctrlrPass,
|
visible: usePassword,
|
||||||
validator: (value) {
|
child: Column(
|
||||||
// Password field can be empty when just updating the profile, not on creation
|
mainAxisAlignment:
|
||||||
if (Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty && value.isEmpty && usePassword) {
|
MainAxisAlignment.start,
|
||||||
return AppLocalizations.of(context).passwordErrorEmpty;
|
crossAxisAlignment:
|
||||||
}
|
CrossAxisAlignment.start,
|
||||||
if (value != ctrlrPass2.value.text) {
|
children: <Widget>[
|
||||||
return AppLocalizations.of(context).passwordErrorMatch;
|
Visibility(
|
||||||
}
|
visible:
|
||||||
return null;
|
Provider.of<ProfileInfoState>(
|
||||||
},
|
context,
|
||||||
|
listen: false)
|
||||||
|
.onion
|
||||||
|
.isNotEmpty,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
CwtchLabel(
|
||||||
|
label: AppLocalizations
|
||||||
|
.of(context)
|
||||||
|
.currentPasswordLabel),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
CwtchPasswordField(
|
||||||
|
controller: ctrlrOldPass,
|
||||||
|
validator: (value) {
|
||||||
|
// Password field can be empty when just updating the profile, not on creation
|
||||||
|
if (Provider.of<ProfileInfoState>(
|
||||||
|
context,
|
||||||
|
listen:
|
||||||
|
false)
|
||||||
|
.onion
|
||||||
|
.isEmpty &&
|
||||||
|
value.isEmpty &&
|
||||||
|
usePassword) {
|
||||||
|
return AppLocalizations
|
||||||
|
.of(context)
|
||||||
|
.passwordErrorEmpty;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
])),
|
||||||
|
CwtchLabel(
|
||||||
|
label:
|
||||||
|
AppLocalizations.of(context)
|
||||||
|
.password1Label),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
CwtchPasswordField(
|
||||||
|
controller: ctrlrPass,
|
||||||
|
validator: (value) {
|
||||||
|
// Password field can be empty when just updating the profile, not on creation
|
||||||
|
if (Provider.of<ProfileInfoState>(
|
||||||
|
context,
|
||||||
|
listen: false)
|
||||||
|
.onion
|
||||||
|
.isEmpty &&
|
||||||
|
value.isEmpty &&
|
||||||
|
usePassword) {
|
||||||
|
return AppLocalizations.of(
|
||||||
|
context)
|
||||||
|
.passwordErrorEmpty;
|
||||||
|
}
|
||||||
|
if (value !=
|
||||||
|
ctrlrPass2.value.text) {
|
||||||
|
return AppLocalizations.of(
|
||||||
|
context)
|
||||||
|
.passwordErrorMatch;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
CwtchLabel(
|
||||||
|
label:
|
||||||
|
AppLocalizations.of(context)
|
||||||
|
.password2Label),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
CwtchPasswordField(
|
||||||
|
controller: ctrlrPass2,
|
||||||
|
validator: (value) {
|
||||||
|
// Password field can be empty when just updating the profile, not on creation
|
||||||
|
if (Provider.of<ProfileInfoState>(
|
||||||
|
context,
|
||||||
|
listen: false)
|
||||||
|
.onion
|
||||||
|
.isEmpty &&
|
||||||
|
value.isEmpty &&
|
||||||
|
usePassword) {
|
||||||
|
return AppLocalizations.of(
|
||||||
|
context)
|
||||||
|
.passwordErrorEmpty;
|
||||||
|
}
|
||||||
|
if (value !=
|
||||||
|
ctrlrPass.value.text) {
|
||||||
|
return AppLocalizations.of(
|
||||||
|
context)
|
||||||
|
.passwordErrorMatch;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
|
]),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
CwtchLabel(label: AppLocalizations.of(context).password2Label),
|
ElevatedButton(
|
||||||
SizedBox(
|
onPressed: _createPressed,
|
||||||
height: 20,
|
style: ElevatedButton.styleFrom(
|
||||||
|
primary: theme
|
||||||
|
.current()
|
||||||
|
.defaultButtonColor()),
|
||||||
|
child: Text(
|
||||||
|
Provider.of<ProfileInfoState>(context)
|
||||||
|
.onion
|
||||||
|
.isEmpty
|
||||||
|
? AppLocalizations.of(context)
|
||||||
|
.addNewProfileBtn
|
||||||
|
: AppLocalizations.of(context)
|
||||||
|
.saveProfileBtn),
|
||||||
),
|
),
|
||||||
CwtchPasswordField(
|
Visibility(
|
||||||
controller: ctrlrPass2,
|
visible: Provider.of<ProfileInfoState>(
|
||||||
validator: (value) {
|
context,
|
||||||
// Password field can be empty when just updating the profile, not on creation
|
listen: false)
|
||||||
if (Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty && value.isEmpty && usePassword) {
|
.onion
|
||||||
return AppLocalizations.of(context).passwordErrorEmpty;
|
.isNotEmpty,
|
||||||
}
|
child: Column(
|
||||||
if (value != ctrlrPass.value.text) {
|
mainAxisAlignment:
|
||||||
return AppLocalizations.of(context).passwordErrorMatch;
|
MainAxisAlignment.start,
|
||||||
}
|
crossAxisAlignment:
|
||||||
return null;
|
CrossAxisAlignment.end,
|
||||||
}),
|
children: [
|
||||||
]),
|
SizedBox(
|
||||||
),
|
height: 20,
|
||||||
SizedBox(
|
),
|
||||||
height: 20,
|
ElevatedButton.icon(
|
||||||
),
|
onPressed: () {
|
||||||
ElevatedButton(
|
showAlertDialog(context);
|
||||||
onPressed: _createPressed,
|
},
|
||||||
style: ElevatedButton.styleFrom(primary: theme.current().defaultButtonColor()),
|
style: ElevatedButton.styleFrom(
|
||||||
child: Text(Provider.of<ProfileInfoState>(context).onion.isEmpty ? AppLocalizations.of(context).addNewProfileBtn : AppLocalizations.of(context).saveProfileBtn),
|
primary: theme
|
||||||
),
|
.current()
|
||||||
Visibility(
|
.defaultButtonColor()),
|
||||||
visible: Provider.of<ProfileInfoState>(context, listen: false).onion.isNotEmpty,
|
icon: Icon(Icons.delete_forever),
|
||||||
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [
|
label: Text(
|
||||||
SizedBox(
|
AppLocalizations.of(context)
|
||||||
height: 20,
|
.deleteBtn),
|
||||||
),
|
)
|
||||||
ElevatedButton.icon(
|
]))
|
||||||
onPressed: () {
|
]))))));
|
||||||
showAlertDialog(context);
|
|
||||||
},
|
|
||||||
style: ElevatedButton.styleFrom(primary: theme.current().defaultButtonColor()),
|
|
||||||
icon: Icon(Icons.delete_forever),
|
|
||||||
label: Text(AppLocalizations.of(context).deleteBtn),
|
|
||||||
)
|
|
||||||
]))
|
|
||||||
]))))));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _copyOnion() {
|
void _copyOnion() {
|
||||||
Clipboard.setData(new ClipboardData(text: Provider.of<ProfileInfoState>(context, listen: false).onion));
|
Clipboard.setData(new ClipboardData(
|
||||||
|
text: Provider.of<ProfileInfoState>(context, listen: false).onion));
|
||||||
// TODO Toast
|
// TODO Toast
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,10 +391,14 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
||||||
if (_formKey.currentState.validate()) {
|
if (_formKey.currentState.validate()) {
|
||||||
if (Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty) {
|
if (Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty) {
|
||||||
if (usePassword == true) {
|
if (usePassword == true) {
|
||||||
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, "be gay do crime");
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -277,7 +411,11 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
||||||
};
|
};
|
||||||
final json = jsonEncode(event);
|
final json = jsonEncode(event);
|
||||||
|
|
||||||
Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(Provider.of<ProfileInfoState>(context, listen: false).onion, json);
|
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
|
||||||
|
@ -288,15 +426,26 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
||||||
};
|
};
|
||||||
final updateNameEventJson = jsonEncode(updateNameEvent);
|
final updateNameEventJson = jsonEncode(updateNameEvent);
|
||||||
|
|
||||||
Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(Provider.of<ProfileInfoState>(context, listen: false).onion, updateNameEventJson);
|
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
|
||||||
|
}
|
||||||
};
|
};
|
||||||
final updatePasswordEventJson = jsonEncode(updatePasswordEvent);
|
final updatePasswordEventJson = jsonEncode(updatePasswordEvent);
|
||||||
|
|
||||||
Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(Provider.of<ProfileInfoState>(context, listen: false).onion, updatePasswordEventJson);
|
Provider.of<FlwtchState>(context, listen: false)
|
||||||
|
.cwtch
|
||||||
|
.SendProfileEvent(
|
||||||
|
Provider.of<ProfileInfoState>(context, listen: false).onion,
|
||||||
|
updatePasswordEventJson);
|
||||||
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
|
@ -310,9 +459,12 @@ showAlertDialog(BuildContext context) {
|
||||||
Widget cancelButton = TextButton(
|
Widget cancelButton = TextButton(
|
||||||
child: Text("Cancel"),
|
child: Text("Cancel"),
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonColor()),
|
backgroundColor:
|
||||||
foregroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonTextColor()),
|
MaterialStateProperty.all(Opaque.current().defaultButtonColor()),
|
||||||
overlayColor: MaterialStateProperty.all(Opaque.current().defaultButtonActiveColor()),
|
foregroundColor: MaterialStateProperty.all(
|
||||||
|
Opaque.current().defaultButtonTextColor()),
|
||||||
|
overlayColor: MaterialStateProperty.all(
|
||||||
|
Opaque.current().defaultButtonActiveColor()),
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.all(20))),
|
padding: MaterialStateProperty.all(EdgeInsets.all(20))),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop(); // dismiss dialog
|
Navigator.of(context).pop(); // dismiss dialog
|
||||||
|
@ -320,9 +472,12 @@ showAlertDialog(BuildContext context) {
|
||||||
);
|
);
|
||||||
Widget continueButton = TextButton(
|
Widget continueButton = TextButton(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonColor()),
|
backgroundColor:
|
||||||
foregroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonTextColor()),
|
MaterialStateProperty.all(Opaque.current().defaultButtonColor()),
|
||||||
overlayColor: MaterialStateProperty.all(Opaque.current().defaultButtonActiveColor()),
|
foregroundColor: MaterialStateProperty.all(
|
||||||
|
Opaque.current().defaultButtonTextColor()),
|
||||||
|
overlayColor: MaterialStateProperty.all(
|
||||||
|
Opaque.current().defaultButtonActiveColor()),
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.all(20))),
|
padding: MaterialStateProperty.all(EdgeInsets.all(20))),
|
||||||
child: Text(AppLocalizations.of(context).deleteProfileConfirmBtn),
|
child: Text(AppLocalizations.of(context).deleteProfileConfirmBtn),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
@ -333,14 +488,7 @@ showAlertDialog(BuildContext context) {
|
||||||
|
|
||||||
// set up the AlertDialog
|
// set up the AlertDialog
|
||||||
AlertDialog alert = AlertDialog(
|
AlertDialog alert = AlertDialog(
|
||||||
backgroundColor: Provider.of<OpaqueTheme>(context, listen: false).current().backgroundPaneColor(),
|
|
||||||
title: Text(AppLocalizations.of(context).deleteProfileConfirmBtn),
|
title: Text(AppLocalizations.of(context).deleteProfileConfirmBtn),
|
||||||
titleTextStyle: TextStyle(
|
|
||||||
color: Provider.of<OpaqueTheme>(context, listen: false).current().mainTextColor(),
|
|
||||||
),
|
|
||||||
contentTextStyle: TextStyle(
|
|
||||||
color: Provider.of<OpaqueTheme>(context, listen: false).current().mainTextColor(),
|
|
||||||
),
|
|
||||||
actions: [
|
actions: [
|
||||||
cancelButton,
|
cancelButton,
|
||||||
continueButton,
|
continueButton,
|
||||||
|
|
|
@ -24,24 +24,31 @@ class _ContactsViewState extends State<ContactsView> {
|
||||||
// Provider.of<ContactListState>(context).onions.forEach((contact) {
|
// Provider.of<ContactListState>(context).onions.forEach((contact) {
|
||||||
// _contacts.putIfAbsent(contact.onion, () => ContactModel(contact);
|
// _contacts.putIfAbsent(contact.onion, () => ContactModel(contact);
|
||||||
// });
|
// });
|
||||||
// .cwtch.GetContacts(widget.profile.onion).then((jsonContacts) {
|
// .cwtch.GetContacts(widget.profile.onion).then((jsonContacts) {
|
||||||
// print("got contact: $jsonContacts");
|
// print("got contact: $jsonContacts");
|
||||||
// setState(() {
|
// setState(() {
|
||||||
// List<dynamic> contacts = jsonDecode(jsonContacts);
|
// List<dynamic> contacts = jsonDecode(jsonContacts);
|
||||||
// contacts.forEach((onion) {
|
// contacts.forEach((onion) {
|
||||||
// _contacts.putIfAbsent(onion['onion'], () => ContactModel(onion: onion['onion'], nickname: onion['name'], status: onion['status']));
|
// _contacts.putIfAbsent(onion['onion'], () => ContactModel(onion: onion['onion'], nickname: onion['name'], status: onion['status']));
|
||||||
// });
|
// });
|
||||||
// });
|
// });
|
||||||
// });
|
// });
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text("%1's contacts".replaceAll("%1", Provider.of<ProfileInfoState>(context).nickname ?? Provider.of<ProfileInfoState>(context).onion ?? '')),//todo
|
title: Text("%1's contacts".replaceAll(
|
||||||
|
"%1",
|
||||||
|
Provider.of<ProfileInfoState>(context).nickname ??
|
||||||
|
Provider.of<ProfileInfoState>(context).onion ??
|
||||||
|
'')), //todo
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(icon: Icon(Icons.copy), onPressed: _copyOnion,),
|
IconButton(
|
||||||
|
icon: Icon(Icons.copy),
|
||||||
|
onPressed: _copyOnion,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
|
@ -58,7 +65,7 @@ class _ContactsViewState extends State<ContactsView> {
|
||||||
stream: Provider.of<FlwtchState>(context).appStatus.contactEvents(),
|
stream: Provider.of<FlwtchState>(context).appStatus.contactEvents(),
|
||||||
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
|
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
|
||||||
final tiles = Provider.of<ContactListState>(context).onions.map(
|
final tiles = Provider.of<ContactListState>(context).onions.map(
|
||||||
(ContactInfoState contact) {
|
(ContactInfoState contact) {
|
||||||
return ChangeNotifierProvider<ContactInfoState>(
|
return ChangeNotifierProvider<ContactInfoState>(
|
||||||
create: (context) => contact,
|
create: (context) => contact,
|
||||||
builder: (context, child) => ContactRow(),
|
builder: (context, child) => ContactRow(),
|
||||||
|
@ -77,20 +84,20 @@ class _ContactsViewState extends State<ContactsView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _pushAddContact() {
|
void _pushAddContact() {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(MaterialPageRoute<void>(
|
||||||
MaterialPageRoute<void>(
|
builder: (BuildContext context) {
|
||||||
builder: (BuildContext context) {
|
return Provider(
|
||||||
return Provider (
|
create: (_) => Provider.of<FlwtchState>(context),
|
||||||
create: (_) => Provider.of<FlwtchState>(context),
|
child: AddContactView(),
|
||||||
child: AddContactView(),
|
);
|
||||||
);
|
},
|
||||||
},
|
));
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _copyOnion() {
|
void _copyOnion() {
|
||||||
final snackBar = SnackBar(content: Text(AppLocalizations.of(context).copiedClipboardNotification));//todo
|
final snackBar = SnackBar(
|
||||||
|
content: Text(
|
||||||
|
AppLocalizations.of(context).copiedClipboardNotification)); //todo
|
||||||
// Find the Scaffold in the widget tree and use it to show a SnackBar.
|
// Find the Scaffold in the widget tree and use it to show a SnackBar.
|
||||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,19 +15,23 @@ class _DoubleColumnViewState extends State<DoubleColumnView> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var flwtch = Provider.of<FlwtchState>(context);
|
var flwtch = Provider.of<FlwtchState>(context);
|
||||||
return Flex(
|
return Flex(
|
||||||
direction: Axis.horizontal,
|
direction: Axis.horizontal,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Flexible(
|
Flexible(
|
||||||
flex: flwtch.columns[0],
|
flex: flwtch.columns[0],
|
||||||
child: ContactsView(),
|
child: ContactsView(),
|
||||||
),
|
),
|
||||||
Flexible(
|
Flexible(
|
||||||
flex: flwtch.columns[1],
|
flex: flwtch.columns[1],
|
||||||
child: flwtch.selectedConversation == "" ?
|
child: flwtch.selectedConversation == ""
|
||||||
Center(child:Text("pick a contact")) : //dev
|
? Center(child: Text("pick a contact"))
|
||||||
Container(child:MessageView(profile:flwtch.selectedProfile, conversationHandle:flwtch.selectedConversation)),
|
: //dev
|
||||||
),
|
Container(
|
||||||
],
|
child: MessageView(
|
||||||
|
profile: flwtch.selectedProfile,
|
||||||
|
conversationHandle: flwtch.selectedConversation)),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -30,27 +31,34 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSettingsList() {
|
Widget _buildSettingsList() {
|
||||||
return Consumer<OpaqueTheme>(builder: (context, theme, child) {
|
return Consumer<Settings>(builder: (context, theme, child) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context).settingLanguage, style: TextStyle(color: theme.current().mainTextColor())),
|
title: Text(AppLocalizations.of(context).settingLanguage,
|
||||||
leading: Icon(Icons.language, color: theme.current().mainTextColor()),
|
style: TextStyle(color: theme.current().mainTextColor())),
|
||||||
|
leading:
|
||||||
|
Icon(Icons.language, color: theme.current().mainTextColor()),
|
||||||
trailing: DropdownButton(
|
trailing: DropdownButton(
|
||||||
value: Provider.of<Settings>(context).locale.languageCode,
|
value: Provider.of<Settings>(context).locale.languageCode,
|
||||||
onChanged: (String newValue) {
|
onChanged: (String newValue) {
|
||||||
setState(() {
|
setState(() {
|
||||||
Provider.of<Settings>(context, listen: false).switchLocale(Locale(newValue, ''));
|
var settings =
|
||||||
|
Provider.of<Settings>(context, listen: false);
|
||||||
|
settings.switchLocale(Locale(newValue, ''));
|
||||||
|
saveSettings(context);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
items: AppLocalizations.supportedLocales.map<DropdownMenuItem<String>>((Locale value) {
|
items: AppLocalizations.supportedLocales
|
||||||
|
.map<DropdownMenuItem<String>>((Locale value) {
|
||||||
return DropdownMenuItem<String>(
|
return DropdownMenuItem<String>(
|
||||||
value: value.languageCode,
|
value: value.languageCode,
|
||||||
child: Text(getLanguageFull(context, value.languageCode)),
|
child: Text(getLanguageFull(context, value.languageCode)),
|
||||||
);
|
);
|
||||||
}).toList())),
|
}).toList())),
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
title: Text(AppLocalizations.of(context).settingTheme, style: TextStyle(color: theme.current().mainTextColor())),
|
title: Text(AppLocalizations.of(context).settingTheme,
|
||||||
|
style: TextStyle(color: theme.current().mainTextColor())),
|
||||||
value: theme.current() == Opaque.light,
|
value: theme.current() == Opaque.light,
|
||||||
onChanged: (bool value) {
|
onChanged: (bool value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
|
@ -58,22 +66,29 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
||||||
} else {
|
} else {
|
||||||
theme.setDark();
|
theme.setDark();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save Settings...
|
||||||
|
saveSettings(context);
|
||||||
},
|
},
|
||||||
secondary: Icon(Icons.lightbulb_outline, color: theme.current().mainTextColor()),
|
secondary: Icon(Icons.lightbulb_outline,
|
||||||
|
color: theme.current().mainTextColor()),
|
||||||
),
|
),
|
||||||
AboutListTile(
|
AboutListTile(
|
||||||
icon: Icon(Icons.info, color: theme.current().mainTextColor()),
|
icon: Icon(Icons.info, color: theme.current().mainTextColor()),
|
||||||
applicationIcon: Padding(
|
applicationIcon: Padding(
|
||||||
padding: EdgeInsets.all(20),
|
padding: EdgeInsets.all(20),
|
||||||
child: Image(
|
child: Image(
|
||||||
image: AssetImage("assets/knott.png"),
|
image: AssetImage("assets/knott.png"),
|
||||||
width: 128,
|
width: 128,
|
||||||
height: 128,
|
height: 128,
|
||||||
)),
|
)),
|
||||||
applicationName: "Cwtch (Flutter UI)",
|
applicationName: "Cwtch (Flutter UI)",
|
||||||
applicationVersion: AppLocalizations.of(context).version.replaceAll("%1", constructVersionString(Provider.of<Settings>(context).packageInfo)),
|
applicationVersion: AppLocalizations.of(context).version.replaceAll(
|
||||||
applicationLegalese: '\u{a9} 2021 Open Privacy Research Society',
|
"%1",
|
||||||
),
|
constructVersionString(
|
||||||
|
Provider.of<Settings>(context).packageInfo)),
|
||||||
|
applicationLegalese: '\u{a9} 2021 Open Privacy Research Society',
|
||||||
|
),
|
||||||
]));
|
]));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -107,3 +122,15 @@ String getLanguageFull(context, String languageCode) {
|
||||||
}
|
}
|
||||||
return languageCode;
|
return languageCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveSettings(context) {
|
||||||
|
var settings = Provider.of<Settings>(context, listen: false);
|
||||||
|
final updateSettingsEvent = {
|
||||||
|
"EventType": "UpdateGlobalSettings",
|
||||||
|
"Data": {"Data": jsonEncode(settings.asJson())},
|
||||||
|
};
|
||||||
|
final updateSettingsEventJson = jsonEncode(updateSettingsEvent);
|
||||||
|
Provider.of<FlwtchState>(context, listen: false)
|
||||||
|
.cwtch
|
||||||
|
.SendAppEvent(updateSettingsEventJson);
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../model.dart';
|
import '../model.dart';
|
||||||
|
@ -6,7 +5,8 @@ import '../opaque.dart';
|
||||||
import '../widgets/messagelist.dart';
|
import '../widgets/messagelist.dart';
|
||||||
|
|
||||||
class MessageView extends StatefulWidget {
|
class MessageView extends StatefulWidget {
|
||||||
const MessageView({Key key, this.profile, this.conversationHandle}) : super(key: key);
|
const MessageView({Key key, this.profile, this.conversationHandle})
|
||||||
|
: super(key: key);
|
||||||
final ProfileInfoState profile;
|
final ProfileInfoState profile;
|
||||||
final String conversationHandle;
|
final String conversationHandle;
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class _MessageViewState extends State<MessageView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold (
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(widget.conversationHandle),
|
title: Text(widget.conversationHandle),
|
||||||
actions: [
|
actions: [
|
||||||
|
@ -35,12 +35,14 @@ class _MessageViewState extends State<MessageView> {
|
||||||
IconButton(icon: Icon(Icons.settings), onPressed: _pushConvoSettings),
|
IconButton(icon: Icon(Icons.settings), onPressed: _pushConvoSettings),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: MessageList(profile: widget.profile, conversationHandle: widget.conversationHandle),
|
body: MessageList(
|
||||||
|
profile: widget.profile,
|
||||||
|
conversationHandle: widget.conversationHandle),
|
||||||
bottomSheet: _buildComposeBox(),
|
bottomSheet: _buildComposeBox(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _pushConvoSettings(){}
|
void _pushConvoSettings() {}
|
||||||
|
|
||||||
void _sendMessage() {
|
void _sendMessage() {
|
||||||
ChatMessage cm = new ChatMessage(o: 1, d: ctrlrCompose.value.text);
|
ChatMessage cm = new ChatMessage(o: 1, d: ctrlrCompose.value.text);
|
||||||
|
@ -49,37 +51,38 @@ class _MessageViewState extends State<MessageView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildComposeBox() {
|
Widget _buildComposeBox() {
|
||||||
return Container (
|
return Container(
|
||||||
color: Opaque.current().backgroundMainColor(),
|
color: Opaque.current().backgroundMainColor(),
|
||||||
height: 100,
|
height: 100,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded (
|
Expanded(child: TextField(controller: ctrlrCompose)),
|
||||||
child:TextField(controller:ctrlrCompose)
|
|
||||||
),
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 80,
|
height: 80,
|
||||||
child: Column(
|
child: Column(children: <Widget>[
|
||||||
children: <Widget>[
|
ElevatedButton(
|
||||||
ElevatedButton(
|
child:
|
||||||
child: Icon(Icons.send, color: Opaque.current().mainTextColor()),
|
Icon(Icons.send, color: Opaque.current().mainTextColor()),
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonColor()),
|
backgroundColor: MaterialStateProperty.all(
|
||||||
), onPressed: _sendMessage,
|
Opaque.current().defaultButtonColor()),
|
||||||
),
|
),
|
||||||
Row (
|
onPressed: _sendMessage,
|
||||||
children: <Widget>[
|
),
|
||||||
SizedBox(width:45, child:ElevatedButton(
|
Row(children: <Widget>[
|
||||||
child: Icon(Icons.emoji_emotions_outlined, color: Opaque.current().mainTextColor())
|
SizedBox(
|
||||||
)),
|
width: 45,
|
||||||
SizedBox(width:45, child:ElevatedButton(
|
child: ElevatedButton(
|
||||||
child: Icon(Icons.attach_file, color: Opaque.current().mainTextColor())
|
child: Icon(Icons.emoji_emotions_outlined,
|
||||||
)),
|
color: Opaque.current().mainTextColor()))),
|
||||||
]
|
SizedBox(
|
||||||
)
|
width: 45,
|
||||||
]
|
child: ElevatedButton(
|
||||||
),
|
child: Icon(Icons.attach_file,
|
||||||
|
color: Opaque.current().mainTextColor()))),
|
||||||
|
])
|
||||||
|
]),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -25,13 +25,19 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold (
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(AppLocalizations.of(context).profileName),
|
title: Text(AppLocalizations.of(context).profileName),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(icon: Icon(Icons.bug_report_outlined), onPressed: _testChangingContactInfo),
|
IconButton(
|
||||||
IconButton(icon: Icon(Icons.lock_open), onPressed: _modalUnlockProfiles,),
|
icon: Icon(Icons.bug_report_outlined),
|
||||||
IconButton(icon: Icon(Icons.settings), onPressed: _pushGlobalSettings),
|
onPressed: _testChangingContactInfo),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.lock_open),
|
||||||
|
onPressed: _modalUnlockProfiles,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.settings), onPressed: _pushGlobalSettings),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
|
@ -39,75 +45,74 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
|
||||||
tooltip: AppLocalizations.of(context).addNewProfileBtn,
|
tooltip: AppLocalizations.of(context).addNewProfileBtn,
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
body: _buildProfileManager(),//_buildSuggestions(),
|
body: _buildProfileManager(), //_buildSuggestions(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _testChangingContactInfo() {
|
void _testChangingContactInfo() {
|
||||||
Provider.of<ProfileListState>(context, listen:false).notifyListeners();
|
Provider.of<ProfileListState>(context, listen: false).notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _pushGlobalSettings() {
|
void _pushGlobalSettings() {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(MaterialPageRoute<void>(
|
||||||
MaterialPageRoute<void>(
|
builder: (BuildContext context) {
|
||||||
builder: (BuildContext context) {
|
return Provider(
|
||||||
return Provider (
|
create: (_) => Provider.of<FlwtchState>(context, listen: false),
|
||||||
create: (_) => Provider.of<FlwtchState>(context),
|
child: GlobalSettingsView(),
|
||||||
child: GlobalSettingsView(),
|
);
|
||||||
);
|
},
|
||||||
},
|
));
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _pushAddEditProfile({onion: ""}) {
|
void _pushAddEditProfile({onion: ""}) {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(MaterialPageRoute<void>(
|
||||||
MaterialPageRoute<void>(
|
builder: (BuildContext context) {
|
||||||
builder: (BuildContext context) {
|
return MultiProvider(
|
||||||
return MultiProvider (
|
providers: [
|
||||||
providers: [
|
ChangeNotifierProvider<ProfileInfoState>(
|
||||||
ChangeNotifierProvider<ProfileInfoState>(create: (_) => ProfileInfoState(onion: onion),),
|
create: (_) => ProfileInfoState(onion: onion),
|
||||||
],
|
),
|
||||||
builder: (context, widget) => AddEditProfileView(),
|
],
|
||||||
);
|
builder: (context, widget) => AddEditProfileView(),
|
||||||
},
|
);
|
||||||
)
|
},
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _modalUnlockProfiles() {
|
void _modalUnlockProfiles() {
|
||||||
showModalBottomSheet<void>(
|
showModalBottomSheet<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
height: 200, // bespoke value courtesy of the [TextField] docs
|
height: 200, // bespoke value courtesy of the [TextField] docs
|
||||||
color: Colors.pink[50],
|
color: Colors.pink[50],
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(AppLocalizations.of(context).enterProfilePassword),
|
Text(AppLocalizations.of(context).enterProfilePassword),
|
||||||
TextField(
|
TextField(
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
controller: ctrlrPassword,
|
controller: ctrlrPassword,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
labelText: AppLocalizations.of(context).password1Label,
|
labelText: AppLocalizations.of(context).password1Label,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
ElevatedButton(
|
||||||
ElevatedButton(
|
child: Text(AppLocalizations.of(context).unlock),
|
||||||
child: Text(AppLocalizations.of(context).unlock),
|
onPressed: () {
|
||||||
onPressed: () {
|
Provider.of<FlwtchState>(context, listen: false)
|
||||||
Provider.of<FlwtchState>(context, listen: false).cwtch.LoadProfiles(ctrlrPassword.value.text);
|
.cwtch
|
||||||
Navigator.pop(context);
|
.LoadProfiles(ctrlrPassword.value.text);
|
||||||
},
|
Navigator.pop(context);
|
||||||
),
|
},
|
||||||
],
|
),
|
||||||
)
|
],
|
||||||
),
|
)),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildProfileManager() {
|
Widget _buildProfileManager() {
|
||||||
|
@ -127,4 +132,4 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
|
||||||
|
|
||||||
return ListView(children: divided);
|
return ListView(children: divided);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,8 @@ class SplashView extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
print("SplashView build()");
|
print("SplashView build()");
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar( title: Text("Cwtch")),
|
appBar: AppBar(title: Text("Cwtch")),
|
||||||
body: Center(
|
body: Center(child: Column(children: <Widget>[Text("Loading Cwtch...")])),
|
||||||
child: Column(
|
|
||||||
children: <Widget>[
|
|
||||||
Text("Loading Cwtch...")
|
|
||||||
])
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,24 +15,27 @@ class _TripleColumnViewState extends State<TripleColumnView> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var flwtch = Provider.of<FlwtchState>(context);
|
var flwtch = Provider.of<FlwtchState>(context);
|
||||||
return Flex(
|
return Flex(direction: Axis.horizontal, children: <Widget>[
|
||||||
direction: Axis.horizontal,
|
Flexible(
|
||||||
children: <Widget>[
|
flex: flwtch.columns[0],
|
||||||
Flexible(
|
child: ProfileMgrView(),
|
||||||
flex: flwtch.columns[0],
|
),
|
||||||
child: ProfileMgrView(),
|
Flexible(
|
||||||
),
|
flex: flwtch.columns[1],
|
||||||
Flexible(
|
child: flwtch.selectedProfile == null
|
||||||
flex: flwtch.columns[1],
|
? Center(child: Text("pick a profile"))
|
||||||
child: flwtch.selectedProfile == null ? Center(child:Text("pick a profile")) : ContactsView(),//dev
|
: ContactsView(), //dev
|
||||||
),
|
),
|
||||||
Flexible(
|
Flexible(
|
||||||
flex: flwtch.columns[2],
|
flex: flwtch.columns[2],
|
||||||
child: flwtch.selectedConversation == "" ?
|
child: flwtch.selectedConversation == ""
|
||||||
Center(child:Text("pick a contact")) : //dev
|
? Center(child: Text("pick a contact"))
|
||||||
Container(child:MessageView(profile:flwtch.selectedProfile, conversationHandle:flwtch.selectedConversation)),
|
: //dev
|
||||||
),
|
Container(
|
||||||
]
|
child: MessageView(
|
||||||
);
|
profile: flwtch.selectedProfile,
|
||||||
|
conversationHandle: flwtch.selectedConversation)),
|
||||||
|
),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app/settings.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../opaque.dart';
|
import '../opaque.dart';
|
||||||
|
|
||||||
// Provides a styled Text Field for use in Form Widgets.
|
// Provides a styled Text Field for use in Form Widgets.
|
||||||
// Callers must provide a text controller, label helper text and a validator.
|
// Callers must provide a text controller, label helper text and a validator.
|
||||||
class CwtchButtonTextField extends StatefulWidget {
|
class CwtchButtonTextField extends StatefulWidget {
|
||||||
CwtchButtonTextField({this.controller, this.onPressed, this.icon, this.tooltip});
|
CwtchButtonTextField(
|
||||||
|
{this.controller, this.onPressed, this.icon, this.tooltip});
|
||||||
final TextEditingController controller;
|
final TextEditingController controller;
|
||||||
final Function onPressed;
|
final Function onPressed;
|
||||||
final Icon icon;
|
final Icon icon;
|
||||||
|
@ -18,33 +20,50 @@ class CwtchButtonTextField extends StatefulWidget {
|
||||||
class _CwtchButtonTextFieldState extends State<CwtchButtonTextField> {
|
class _CwtchButtonTextFieldState extends State<CwtchButtonTextField> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<OpaqueTheme>(builder: (context, theme, child) {
|
return Consumer<Settings>(builder: (context, theme, child) {
|
||||||
return TextField(
|
return TextField(
|
||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
onPressed: widget.onPressed,
|
onPressed: widget.onPressed,
|
||||||
icon: widget.icon,
|
icon: widget.icon,
|
||||||
tooltip: widget.tooltip,
|
tooltip: widget.tooltip,
|
||||||
enableFeedback: true,
|
enableFeedback: true,
|
||||||
color: theme.current().mainTextColor(),
|
color: theme.current().mainTextColor(),
|
||||||
highlightColor: theme.current().defaultButtonColor(),
|
highlightColor: theme.current().defaultButtonColor(),
|
||||||
focusColor: theme.current().defaultButtonActiveColor(),
|
focusColor: theme.current().defaultButtonActiveColor(),
|
||||||
splashColor: theme.current().defaultButtonActiveColor(),
|
splashColor: theme.current().defaultButtonActiveColor(),
|
||||||
),
|
),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
filled: true,
|
filled: true,
|
||||||
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor(), width: 3.0)),
|
focusedBorder: OutlineInputBorder(
|
||||||
focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor(), width: 3.0)),
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor(), width: 3.0)),
|
borderSide: BorderSide(
|
||||||
errorStyle: TextStyle (color: theme.current().textfieldErrorColor(), fontWeight: FontWeight.bold,),
|
color: theme.current().textfieldBorderColor(), width: 3.0)),
|
||||||
fillColor: theme.current().textfieldBackgroundColor(),
|
focusedErrorBorder: OutlineInputBorder(
|
||||||
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor(), width: 3.0))),
|
borderSide: BorderSide(
|
||||||
|
color: theme.current().textfieldErrorColor(), width: 3.0)),
|
||||||
style: TextStyle(color: theme.current().mainTextColor(), backgroundColor: theme.current().textfieldBackgroundColor()),
|
errorBorder: OutlineInputBorder(
|
||||||
);
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: theme.current().textfieldErrorColor(), width: 3.0)),
|
||||||
|
errorStyle: TextStyle(
|
||||||
|
color: theme.current().textfieldErrorColor(),
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
fillColor: theme.current().textfieldBackgroundColor(),
|
||||||
|
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: theme.current().textfieldBorderColor(),
|
||||||
|
width: 3.0))),
|
||||||
|
style: TextStyle(
|
||||||
|
color: theme.current().mainTextColor(),
|
||||||
|
backgroundColor: theme.current().textfieldBackgroundColor()),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,11 +20,28 @@ class _ContactRowState extends State<ContactRow> {
|
||||||
width: 60,
|
width: 60,
|
||||||
height: 60,
|
height: 60,
|
||||||
child: ClipOval(
|
child: ClipOval(
|
||||||
child: SizedBox(width:60, height:60, child:Container(color:Colors.white, width: 60, height: 60, child: Image(image: AssetImage("assets/profiles/001-centaur.png"), width:50,height:50,))),
|
child: SizedBox(
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
child: Container(
|
||||||
|
color: Colors.white,
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
child: Image(
|
||||||
|
image: AssetImage("assets/profiles/001-centaur.png"),
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
))),
|
||||||
//child: SizedBox(width:60, height:60, child:Container(color:Colors.white, width: 60, height: 60, child: Image(image: AssetImage(contact.imagePath), width:50,height:50,))),
|
//child: SizedBox(width:60, height:60, child:Container(color:Colors.white, width: 60, height: 60, child: Image(image: AssetImage(contact.imagePath), width:50,height:50,))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: contact.isInvitation != null && contact.isInvitation ? Column(children:<Widget>[Icon(Icons.favorite, color: Opaque.current().mainTextColor()),Icon(Icons.delete, color: Opaque.current().mainTextColor())]) : Text("99+"),//(nb: Icons.create is a pencil and we use it for "edit", not create)
|
trailing: contact.isInvitation != null && contact.isInvitation
|
||||||
|
? Column(children: <Widget>[
|
||||||
|
Icon(Icons.favorite, color: Opaque.current().mainTextColor()),
|
||||||
|
Icon(Icons.delete, color: Opaque.current().mainTextColor())
|
||||||
|
])
|
||||||
|
: Text(
|
||||||
|
"99+"), //(nb: Icons.create is a pencil and we use it for "edit", not create)
|
||||||
title: Text(
|
title: Text(
|
||||||
contact.nickname,
|
contact.nickname,
|
||||||
style: Provider.of<FlwtchState>(context).biggerFont,
|
style: Provider.of<FlwtchState>(context).biggerFont,
|
||||||
|
@ -32,7 +49,7 @@ class _ContactRowState extends State<ContactRow> {
|
||||||
subtitle: Text(contact.status),
|
subtitle: Text(contact.status),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
var flwtch = Provider.of<FlwtchState>(context, listen:false);
|
var flwtch = Provider.of<FlwtchState>(context, listen: false);
|
||||||
flwtch.setState(() => flwtch.selectedConversation = contact.onion);
|
flwtch.setState(() => flwtch.selectedConversation = contact.onion);
|
||||||
|
|
||||||
// case 2/3 handled by Double/TripleColumnView respectively
|
// case 2/3 handled by Double/TripleColumnView respectively
|
||||||
|
@ -47,7 +64,10 @@ class _ContactRowState extends State<ContactRow> {
|
||||||
MaterialPageRoute<void>(
|
MaterialPageRoute<void>(
|
||||||
builder: (BuildContext builderContext) {
|
builder: (BuildContext builderContext) {
|
||||||
return MultiProvider(
|
return MultiProvider(
|
||||||
providers: [ChangeNotifierProvider<ProfileInfoState>(create: (_) => Provider.of<ProfileInfoState>(context)),],
|
providers: [
|
||||||
|
ChangeNotifierProvider<ProfileInfoState>(
|
||||||
|
create: (_) => Provider.of<ProfileInfoState>(context)),
|
||||||
|
],
|
||||||
child: MessageView(conversationHandle: handle),
|
child: MessageView(conversationHandle: handle),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,12 +2,13 @@ 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 '../opaque.dart';
|
import '../opaque.dart';
|
||||||
|
import '../settings.dart';
|
||||||
|
|
||||||
// Provides a styled Label
|
// Provides a styled Label
|
||||||
// Callers must provide a label text
|
// Callers must provide a label text
|
||||||
// TODO: Integrate this with a settings "zoom" / accessibility setting
|
// TODO: Integrate this with a settings "zoom" / accessibility setting
|
||||||
class CwtchLabel extends StatefulWidget {
|
class CwtchLabel extends StatefulWidget {
|
||||||
CwtchLabel({ this.label});
|
CwtchLabel({this.label});
|
||||||
final String label;
|
final String label;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -15,16 +16,13 @@ class CwtchLabel extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CwtchLabelState extends State<CwtchLabel> {
|
class _CwtchLabelState extends State<CwtchLabel> {
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<OpaqueTheme> (
|
return Consumer<Settings>(builder: (context, theme, child) {
|
||||||
builder: (context, theme, child) {
|
return Text(
|
||||||
return Text(
|
widget.label,
|
||||||
widget.label,
|
style: TextStyle(fontSize: 20, color: theme.current().mainTextColor()),
|
||||||
style: TextStyle(fontSize: 20, color: theme.current().mainTextColor()),
|
);
|
||||||
);
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,16 +16,23 @@ class MessageBubble extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MessageBubbleState extends State<MessageBubble> {
|
class _MessageBubbleState extends State<MessageBubble> {
|
||||||
String d="", ts="";
|
String d = "", ts = "";
|
||||||
bool ack=false;
|
bool ack = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
|
|
||||||
print("requesting message " + widget.messageIndex.toString());
|
print("requesting message " + widget.messageIndex.toString());
|
||||||
Provider.of<FlwtchState>(context).cwtch.GetMessage(widget.profile.onion, widget.contactOnion, widget.messageIndex).then((jsonMessage){
|
Provider.of<FlwtchState>(context)
|
||||||
print("got message: " + widget.messageIndex.toString() + ": " + jsonMessage);
|
.cwtch
|
||||||
|
.GetMessage(
|
||||||
|
widget.profile.onion, widget.contactOnion, widget.messageIndex)
|
||||||
|
.then((jsonMessage) {
|
||||||
|
print("got message: " +
|
||||||
|
widget.messageIndex.toString() +
|
||||||
|
": " +
|
||||||
|
jsonMessage);
|
||||||
dynamic messageWrapper = jsonDecode(jsonMessage);
|
dynamic messageWrapper = jsonDecode(jsonMessage);
|
||||||
dynamic message = jsonDecode(messageWrapper['Message']);
|
dynamic message = jsonDecode(messageWrapper['Message']);
|
||||||
setState(() {
|
setState(() {
|
||||||
|
@ -57,11 +64,12 @@ class _MessageBubbleState extends State<MessageBubble> {
|
||||||
),
|
),
|
||||||
subtitle: Row(
|
subtitle: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(""+widget.messageIndex.toString()),
|
Text("" + widget.messageIndex.toString()),
|
||||||
ack ? Icon(Icons.check_circle_outline,
|
ack
|
||||||
color: Opaque.current().mainTextColor()) : Icon(
|
? Icon(Icons.check_circle_outline,
|
||||||
Icons.hourglass_bottom_outlined,
|
color: Opaque.current().mainTextColor())
|
||||||
color: Opaque.current().mainTextColor())
|
: Icon(Icons.hourglass_bottom_outlined,
|
||||||
|
color: Opaque.current().mainTextColor())
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -10,7 +10,8 @@ class MessageList extends StatefulWidget {
|
||||||
final ProfileInfoState profile;
|
final ProfileInfoState profile;
|
||||||
final String conversationHandle;
|
final String conversationHandle;
|
||||||
|
|
||||||
const MessageList({Key key, this.profile, this.conversationHandle}) : super(key: key);
|
const MessageList({Key key, this.profile, this.conversationHandle})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_MessageListState createState() => _MessageListState();
|
_MessageListState createState() => _MessageListState();
|
||||||
|
@ -30,18 +31,17 @@ class _MessageListState extends State<MessageList> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return ProxyProvider0(
|
return ProxyProvider0(
|
||||||
update: (_, __) => MessageCounter(conversationNumMessages),
|
update: (_, __) => MessageCounter(conversationNumMessages),
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
itemCount: conversationNumMessages,
|
itemCount: conversationNumMessages,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return MessageBubble(
|
return MessageBubble(
|
||||||
profile: Provider.of<ProfileInfoState>(context),
|
profile: Provider.of<ProfileInfoState>(context),
|
||||||
contactOnion: widget.conversationHandle,
|
contactOnion: widget.conversationHandle,
|
||||||
messageIndex: index,
|
messageIndex: index,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future _updateMessageCount(BuildContext context) async {
|
Future _updateMessageCount(BuildContext context) async {
|
||||||
|
@ -50,8 +50,14 @@ class _MessageListState extends State<MessageList> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Provider.of<FlwtchState>(context, listen: false).cwtch.NumMessages(Provider.of<ProfileInfoState>(context, listen: false).onion, widget.conversationHandle).then((n) {
|
Provider.of<FlwtchState>(context, listen: false)
|
||||||
if (n != conversationNumMessages) setState(() => conversationNumMessages = n);
|
.cwtch
|
||||||
|
.NumMessages(
|
||||||
|
Provider.of<ProfileInfoState>(context, listen: false).onion,
|
||||||
|
widget.conversationHandle)
|
||||||
|
.then((n) {
|
||||||
|
if (n != conversationNumMessages)
|
||||||
|
setState(() => conversationNumMessages = n);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,4 +65,4 @@ class _MessageListState extends State<MessageList> {
|
||||||
class MessageCounter {
|
class MessageCounter {
|
||||||
MessageCounter(this.x);
|
MessageCounter(this.x);
|
||||||
int x = 0;
|
int x = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,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 '../opaque.dart';
|
import '../opaque.dart';
|
||||||
|
import '../settings.dart';
|
||||||
|
|
||||||
// Provides a styled Password Input Field for use in Form Widgets.
|
// Provides a styled Password Input Field for use in Form Widgets.
|
||||||
// Callers must provide a text controller, label helper text and a validator.
|
// Callers must provide a text controller, label helper text and a validator.
|
||||||
|
@ -17,7 +18,7 @@ class CwtchPasswordField extends StatefulWidget {
|
||||||
class _CwtchTextFieldState extends State<CwtchPasswordField> {
|
class _CwtchTextFieldState extends State<CwtchPasswordField> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<OpaqueTheme>(builder: (context, theme, child) {
|
return Consumer<Settings>(builder: (context, theme, child) {
|
||||||
return TextFormField(
|
return TextFormField(
|
||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
validator: widget.validator,
|
validator: widget.validator,
|
||||||
|
@ -25,18 +26,32 @@ class _CwtchTextFieldState extends State<CwtchPasswordField> {
|
||||||
enableSuggestions: false,
|
enableSuggestions: false,
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
errorStyle: TextStyle(
|
errorStyle: TextStyle(
|
||||||
color: theme.current().textfieldErrorColor(),
|
color: theme.current().textfieldErrorColor(),
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor(), width: 3.0)),
|
focusedBorder: OutlineInputBorder(
|
||||||
focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor(), width: 3.0)),
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor(), width: 3.0)),
|
borderSide: BorderSide(
|
||||||
filled: true,
|
color: theme.current().textfieldBorderColor(), width: 3.0)),
|
||||||
fillColor: theme.current().textfieldBackgroundColor(),
|
focusedErrorBorder: OutlineInputBorder(
|
||||||
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor(), width: 3.0)),
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: theme.current().textfieldErrorColor(), width: 3.0)),
|
||||||
|
errorBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: theme.current().textfieldErrorColor(), width: 3.0)),
|
||||||
|
filled: true,
|
||||||
|
fillColor: theme.current().textfieldBackgroundColor(),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: theme.current().textfieldBorderColor(), width: 3.0)),
|
||||||
),
|
),
|
||||||
style: TextStyle(color: theme.current().mainTextColor(), backgroundColor: theme.current().textfieldBackgroundColor()),
|
style: TextStyle(
|
||||||
|
color: theme.current().mainTextColor(),
|
||||||
|
backgroundColor: theme.current().textfieldBackgroundColor()),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:provider/provider.dart';
|
||||||
import '../main.dart';
|
import '../main.dart';
|
||||||
import '../model.dart';
|
import '../model.dart';
|
||||||
import '../opaque.dart';
|
import '../opaque.dart';
|
||||||
|
import '../settings.dart';
|
||||||
|
|
||||||
class ProfileRow extends StatefulWidget {
|
class ProfileRow extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
|
@ -22,13 +23,30 @@ class _ProfileRowState extends State<ProfileRow> {
|
||||||
width: 60,
|
width: 60,
|
||||||
height: 60,
|
height: 60,
|
||||||
child: ClipOval(
|
child: ClipOval(
|
||||||
child: SizedBox(width:60, height:60, child:Container(color:Colors.white, width: 60, height: 60, child: Image(image: AssetImage("assets/" + profile.imagePath), width:50,height:50,))),
|
child: SizedBox(
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
child: Container(
|
||||||
|
color: Colors.white,
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
child: Image(
|
||||||
|
image: AssetImage("assets/" + profile.imagePath),
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
))),
|
||||||
),
|
),
|
||||||
) ,
|
),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
icon: Icon(Icons.create, color: Provider.of<OpaqueTheme>(context).current().mainTextColor()),
|
icon: Icon(Icons.create,
|
||||||
onPressed: () { _pushAddEditProfile(onion: profile.onion, displayName: profile.nickname, profileImage: profile.imagePath); },
|
color: Provider.of<Settings>(context).current().mainTextColor()),
|
||||||
),//(nb: Icons.create is a pencil and we use it for "edit", not create)
|
onPressed: () {
|
||||||
|
_pushAddEditProfile(
|
||||||
|
onion: profile.onion,
|
||||||
|
displayName: profile.nickname,
|
||||||
|
profileImage: profile.imagePath);
|
||||||
|
},
|
||||||
|
), //(nb: Icons.create is a pencil and we use it for "edit", not create)
|
||||||
title: Text(
|
title: Text(
|
||||||
profile.nickname,
|
profile.nickname,
|
||||||
style: Provider.of<FlwtchState>(context).biggerFont,
|
style: Provider.of<FlwtchState>(context).biggerFont,
|
||||||
|
@ -36,7 +54,7 @@ class _ProfileRowState extends State<ProfileRow> {
|
||||||
subtitle: Text(profile.onion),
|
subtitle: Text(profile.onion),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
var flwtch = Provider.of<FlwtchState>(context, listen:false);
|
var flwtch = Provider.of<FlwtchState>(context, listen: false);
|
||||||
flwtch.cwtch.SelectProfile(profile.onion);
|
flwtch.cwtch.SelectProfile(profile.onion);
|
||||||
flwtch.setState(() {
|
flwtch.setState(() {
|
||||||
flwtch.selectedProfile = profile;
|
flwtch.selectedProfile = profile;
|
||||||
|
@ -44,8 +62,12 @@ class _ProfileRowState extends State<ProfileRow> {
|
||||||
});
|
});
|
||||||
|
|
||||||
switch (flwtch.columns.length) {
|
switch (flwtch.columns.length) {
|
||||||
case 1: _pushContactList(profile, false); break;
|
case 1:
|
||||||
case 2: _pushContactList(profile, true); break;
|
_pushContactList(profile, false);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
_pushContactList(profile, true);
|
||||||
|
break;
|
||||||
} // case 3: handled by TripleColumnView
|
} // case 3: handled by TripleColumnView
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -59,28 +81,33 @@ class _ProfileRowState extends State<ProfileRow> {
|
||||||
return MultiProvider(
|
return MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
ChangeNotifierProvider<ProfileInfoState>.value(value: profile),
|
ChangeNotifierProvider<ProfileInfoState>.value(value: profile),
|
||||||
ChangeNotifierProvider<ContactListState>(create: (_) => ContactListState(Provider.of<FlwtchState>(buildcontext).cwtch, profile.onion),),
|
ChangeNotifierProvider<ContactListState>(
|
||||||
|
create: (_) => ContactListState(
|
||||||
|
Provider.of<FlwtchState>(buildcontext).cwtch,
|
||||||
|
profile.onion),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
builder: (context, widget) => includeDoublePane ? DoubleColumnView() : ContactsView(),
|
builder: (context, widget) =>
|
||||||
|
includeDoublePane ? DoubleColumnView() : ContactsView(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void _pushAddEditProfile({onion: "", displayName: "", profileImage: ""}) {
|
void _pushAddEditProfile({onion: "", displayName: "", profileImage: ""}) {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(MaterialPageRoute<void>(
|
||||||
MaterialPageRoute<void>(
|
builder: (BuildContext context) {
|
||||||
builder: (BuildContext context) {
|
return MultiProvider(
|
||||||
return MultiProvider (
|
providers: [
|
||||||
providers: [
|
ChangeNotifierProvider<ProfileInfoState>(
|
||||||
ChangeNotifierProvider<ProfileInfoState>(create: (_) => ProfileInfoState(onion: onion, nickname:displayName, imagePath: profileImage),),
|
create: (_) => ProfileInfoState(
|
||||||
],
|
onion: onion, nickname: displayName, imagePath: profileImage),
|
||||||
builder: (context, widget) => AddEditProfileView(),
|
),
|
||||||
);
|
],
|
||||||
},
|
builder: (context, widget) => AddEditProfileView(),
|
||||||
)
|
);
|
||||||
);
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../opaque.dart';
|
import '../opaque.dart';
|
||||||
|
import '../settings.dart';
|
||||||
|
|
||||||
// Provides a styled Text Field for use in Form Widgets.
|
// Provides a styled Text Field for use in Form Widgets.
|
||||||
// Callers must provide a text controller, label helper text and a validator.
|
// Callers must provide a text controller, label helper text and a validator.
|
||||||
|
@ -17,26 +18,44 @@ class CwtchTextField extends StatefulWidget {
|
||||||
class _CwtchTextFieldState extends State<CwtchTextField> {
|
class _CwtchTextFieldState extends State<CwtchTextField> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<OpaqueTheme>(builder: (context, theme, child) {
|
return Consumer<Settings>(builder: (context, theme, child) {
|
||||||
return TextFormField(
|
return TextFormField(
|
||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
validator: widget.validator,
|
validator: widget.validator,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
labelText: widget.labelText,
|
||||||
labelText: widget.labelText,
|
labelStyle: TextStyle(
|
||||||
labelStyle: TextStyle(color: theme.current().mainTextColor(), backgroundColor: theme.current().textfieldBackgroundColor()),
|
color: theme.current().mainTextColor(),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
backgroundColor: theme.current().textfieldBackgroundColor()),
|
||||||
filled: true,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor(), width: 3.0)),
|
filled: true,
|
||||||
focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor(), width: 3.0)),
|
focusedBorder: OutlineInputBorder(
|
||||||
errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor(), width: 3.0)),
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
errorStyle: TextStyle (color: theme.current().textfieldErrorColor(), fontWeight: FontWeight.bold,),
|
borderSide: BorderSide(
|
||||||
fillColor: theme.current().textfieldBackgroundColor(),
|
color: theme.current().textfieldBorderColor(), width: 3.0)),
|
||||||
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
|
focusedErrorBorder: OutlineInputBorder(
|
||||||
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(15.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor(), width: 3.0))),
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
style: TextStyle(color: theme.current().mainTextColor(), backgroundColor: theme.current().textfieldBackgroundColor()),
|
color: theme.current().textfieldErrorColor(), width: 3.0)),
|
||||||
);
|
errorBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: theme.current().textfieldErrorColor(), width: 3.0)),
|
||||||
|
errorStyle: TextStyle(
|
||||||
|
color: theme.current().textfieldErrorColor(),
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
fillColor: theme.current().textfieldBackgroundColor(),
|
||||||
|
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: theme.current().textfieldBorderColor(),
|
||||||
|
width: 3.0))),
|
||||||
|
style: TextStyle(
|
||||||
|
color: theme.current().mainTextColor(),
|
||||||
|
backgroundColor: theme.current().textfieldBackgroundColor()),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,17 +15,15 @@ class _TorStatusState extends State<TorStatusLabel> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Builder(
|
return Builder(
|
||||||
builder: (context2) => StreamBuilder<String>(
|
builder: (context2) => StreamBuilder<String>(
|
||||||
stream: Provider.of<FlwtchState>(context).appStatus.torStatus(),
|
stream: Provider.of<FlwtchState>(context).appStatus.torStatus(),
|
||||||
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
|
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
|
||||||
return Text(
|
return Text(
|
||||||
snapshot.hasData ?
|
snapshot.hasData
|
||||||
snapshot.data : AppLocalizations.of(context).loadingTor,
|
? snapshot.data
|
||||||
style: Theme
|
: AppLocalizations.of(context).loadingTor,
|
||||||
.of(context)
|
style: Theme.of(context).textTheme.headline4,
|
||||||
.textTheme
|
);
|
||||||
.headline4,
|
},
|
||||||
);
|
));
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue