Merge branch 'trunk' of git.openprivacy.ca:flutter/flutter_app into androidservice

This commit is contained in:
erinn 2021-06-01 14:01:56 -07:00
commit ee303f9d11
29 changed files with 228 additions and 119 deletions

View File

@ -53,13 +53,18 @@ steps:
- name: deps
path: /root/.pub-cache
commands:
- flutter build linux
- flutter build linux --dart-define BUILD_VER=`cat VERSION` --dart-define BUILD_DATE=`cat BUILDDATE`
- mkdir deploy/linux
- cp -r build/linux/x64/release/bundle/* deploy/linux
- cp linux/cwtch.desktop deploy/linux
- cp linux/cwtch.png deploy/linux
- cp linux/libCwtch.so deploy/linux/lib/
- cp /sdks/flutter/bin/cache/artifacts/engine/linux-x64/icudtl.dat deploy/linux
- cp tor deploy/linux
- cd deploy
- mv linux cwtch
- tar -czf cwtch-`cat ../VERSION`.tar.gz cwtch
- rm -r cwtch
- name: build-android
image: cirrusci/flutter:dev
@ -77,13 +82,12 @@ steps:
- echo $upload_jks_file_b64 > upload-keystore.jks.b64
- base64 -i --decode upload-keystore.jks.b64 > android/app/upload-keystore.jks
- sed -i "s/%jks-password%/$upload_jks_pass/g" android/key.properties
- flutter build appbundle
- flutter build appbundle --dart-define BUILD_VER=`cat VERSION` --dart-define BUILD_DATE=`cat BUILDDATE`
# cant do debug for final release, this is just a stop gap
- flutter build apk
- flutter build apk --dart-define BUILD_VER=`cat VERSION` --dart-define BUILD_DATE=`cat BUILDDATE`
# or build apk --split-per-abi ?
- mkdir deploy/android
- cp build/app/outputs/bundle/release/app-release.aab deploy/android
- cp build/app/outputs/apk/release/app-release.apk deploy/android
- cp build/app/outputs/bundle/release/app-release.aab deploy/
- cp build/app/outputs/apk/release/app-release.apk deploy/
#- cp build/app/outputs/flutter-apk/app-debug.apk deploy/android
- name: widget-tests
@ -179,7 +183,7 @@ clone:
steps:
- name: clone
image: openpriv/flutter-desktop:windows-sdk30-fdev2.2rc
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
environment:
buildbot_key_b64:
from_secret: buildbot_key_b64
@ -197,7 +201,7 @@ steps:
- git checkout $DRONE_COMMIT
- name: fetch
image: openpriv/flutter-desktop:windows-sdk30-fdev2.2rc
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
commands:
- powershell -command "Invoke-WebRequest -Uri https://www.torproject.org/dist/torbrowser/10.0.16/tor-win32-0.4.5.7.zip -OutFile tor.zip"
- powershell -command "if ((Get-FileHash tor.zip -Algorithm sha512).Hash -ne '2b7d683f036d0fec149f1d2bdfcf5b7ef4c337005a2b685c056b00047fdb2b57d4c25b8559ad7ef5c7a030b273934be82a9f83ef6e391f5d7d13d8d6c83e8048' ) { Write-Error 'tor.zip sha512sum mismatch' }"
@ -206,19 +210,23 @@ steps:
- .\fetch-libcwtch-go.ps1
-
- name: build-windows
image: openpriv/flutter-desktop:windows-beta
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
commands:
- flutter pub get
- flutter build windows
# flwtch-`cat VERSION`-`cat BUILDDATE`
- $Env:buildname = 'flwtch-win-'
- $Env:buildname += type .\VERSION
- $Env:version += type .\VERSION
- $Env:buildname += $Env:version
- $Env:buildname += '-'
- $Env:buildname += type .\BUILDDATE
- $Env:builddate += type .\BUILDDATE
- $Env:buildname += $Env:builddate
- $Env:builddir += $Env:buildname
- $Env:zip = 'flwtch.zip'
- $Env:zip = 'cwtch-'
- $Env:zip += $Env:version
- $Env:zip += '.zip'
- $Env:sha = $Env:zip
- $Env:sha += '.sha512'
- flutter build windows --dart-define BUILD_VER=$Env:version --dart-define BUILD_DATE=$Env:builddate
- mkdir deploy
- move build\\windows\\runner\\Release $Env:builddir
- copy windows\libCwtch.dll $Env:builddir
@ -230,7 +238,7 @@ steps:
- move $Env:sha deploy\$Env:builddir
- name: deploy-windows
image: openpriv/flutter-desktop:windows-sdk30-fdev2.2rc
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
when:
event: push
status: [ success ]

View File

@ -1 +1 @@
v0.0.2-43-ga98b5de-2021-05-28-21-21
v0.0.2-45-g4f625c7-2021-05-31-23-30

6
lib/config.dart Normal file
View File

@ -0,0 +1,6 @@
const dev_version = "development";
class EnvironmentConfig {
static const BUILD_VER = String.fromEnvironment('BUILD_VER', defaultValue: dev_version);
static const BUILD_DATE = String.fromEnvironment('BUILD_DATE', defaultValue: "now");
}

View File

@ -1,4 +1,5 @@
import 'dart:convert';
import 'package:cwtch/models/servers.dart';
import 'package:cwtch/notification_manager.dart';
import 'package:provider/provider.dart';
@ -40,18 +41,26 @@ class CwtchNotifier {
imagePath: data["picture"],
isBlocked: data["authorization"] == "blocked",
isInvitation: data["authorization"] == "unknown",
savePeerHistory: data["saveConversationHistory"],
savePeerHistory: data["saveConversationHistory"] == null ? "DeleteHistoryConfirmed" : data["saveConversationHistory"],
numMessages: int.parse(data["numMessages"]),
numUnread: int.parse(data["unread"]),
isGroup: data["isGroup"],
isGroup: data["isGroup"] == true,
server: data["groupServer"],
lastMessageTime: DateTime.now(), //show at the top of the contact list even if no messages yet
));
break;
case "GroupCreated":
// Retrieve Server Status from Cache...
String status = "";
ServerInfoState? serverInfoState = profileCN.getProfile(data["ProfileOnion"])?.serverList.getServer(data["GroupServer"]);
if (serverInfoState != null) {
status = serverInfoState.status;
}
if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"]) == null) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(data["ProfileOnion"], data["GroupID"],
isInvitation: false, imagePath: data["PicturePath"], nickname: data["GroupName"], server: data["GroupServer"], isGroup: true, lastMessageTime: DateTime.now()));
isInvitation: false, imagePath: data["PicturePath"], nickname: data["GroupName"], status: status, server: data["GroupServer"], isGroup: true, lastMessageTime: DateTime.now()));
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now());
}
break;
@ -175,9 +184,23 @@ class CwtchNotifier {
String inviteJson = new String.fromCharCodes(base64Decode(invite.substring(5)));
dynamic groupInvite = jsonDecode(inviteJson);
print("new group invite: $groupInvite");
// Retrieve Server Status from Cache...
String status = "";
ServerInfoState? serverInfoState = profileCN.getProfile(data["ProfileOnion"])?.serverList.getServer(groupInvite["ServerHost"]);
if (serverInfoState != null) {
status = serverInfoState.status;
}
if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(groupInvite["GroupID"]) == null) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(data["ProfileOnion"], groupInvite["GroupID"],
isInvitation: true, imagePath: data["PicturePath"], nickname: groupInvite["GroupName"], server: groupInvite["ServerHost"], isGroup: true, lastMessageTime: DateTime.now()));
isInvitation: true,
imagePath: data["PicturePath"],
nickname: groupInvite["GroupName"],
server: groupInvite["ServerHost"],
status: status,
isGroup: true,
lastMessageTime: DateTime.now()));
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(groupInvite["GroupID"], DateTime.now());
}
}
@ -189,6 +212,8 @@ class CwtchNotifier {
break;
case "ServerStateChange":
print("server state change: $data");
// Update the Server Cache
profileCN.getProfile(data["ProfileOnion"])?.updateServerStatusCache(data["GroupServer"], data["ConnectionState"]);
profileCN.getProfile(data["ProfileOnion"])?.contactList.contacts.forEach((contact) {
if (contact.isGroup == true && contact.server == data["GroupServer"]) {
contact.status = data["ConnectionState"];
@ -205,6 +230,16 @@ class CwtchNotifier {
print("unhandled set group attribute event: $type $data");
}
break;
case "NewRetValMessageFromPeer":
if (data["Path"] == "name") {
// Update locally on the UI...
if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"]) != null) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.nickname = data["Data"];
}
} else {
print("unhandled peer attribute event: $type $data");
}
break;
default:
print("unhandled event: $type $data");
}

View File

@ -9,6 +9,8 @@ import 'package:path/path.dart' as path;
import 'package:ffi/ffi.dart';
import 'package:cwtch/cwtch/cwtch.dart';
import '../config.dart';
/////////////////////
/// Cwtch API ///
/////////////////////
@ -84,7 +86,10 @@ class CwtchFfi implements Cwtch {
home = (envVars['UserProfile'])!;
bundledTor = "Tor\\Tor\\tor.exe";
}
var cwtchDir = path.join(home, ".cwtch/dev/");
var cwtchDir = path.join(home, ".cwtch");
if (EnvironmentConfig.BUILD_VER == dev_version) {
cwtchDir = path.join(cwtchDir, "dev");
}
print("cwtchDir $cwtchDir");
var startCwtchC = library.lookup<NativeFunction<start_cwtch_function>>("c_StartCwtch");

View File

@ -1,6 +1,7 @@
import 'dart:convert';
import 'dart:io';
import 'package:cwtch/config.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:async';
@ -44,7 +45,10 @@ class CwtchGomobile implements Cwtch {
// ignore: non_constant_identifier_names
Future<void> Start() async {
print("gomobile.dart: Start()...");
var cwtchDir = path.join((await androidHomeDirectory).path, ".cwtch/dev/");
var cwtchDir = path.join((await androidHomeDirectory).path, ".cwtch");
if (EnvironmentConfig.BUILD_VER == dev_version) {
cwtchDir = path.join(cwtchDir, "dev");
}
String torPath = path.join(await androidLibraryDir, "libtor.so");
print("gomobile.dart: Start invokeMethod Start($cwtchDir, $torPath)...");
cwtchPlatform.invokeMethod("Start", {"appDir": cwtchDir, "torPath": torPath});

View File

@ -29,6 +29,7 @@ class ErrorHandler extends ChangeNotifier {
break;
case importBundleErrorPrefix:
handleImportBundleError(errorType);
break;
}
notifyListeners();
@ -49,6 +50,7 @@ class ErrorHandler extends ChangeNotifier {
break;
case successErrorType:
explicitAddContactSuccess = true;
importBundleSuccess = true;
break;
}
}

View File

@ -75,10 +75,12 @@
"invitation": "Einladung",
"invitationLabel": "Einladung",
"inviteBtn": "Einladen",
"inviteToGroup": "",
"inviteToGroupLabel": "In die Gruppe einladen",
"joinGroup": "Gruppe beitreten",
"joinGroupTab": "Einer Gruppe beitreten",
"largeTextLabel": "Groß",
"leaveGroup": "",
"listsBtn": "Listen",
"loadingTor": "Tor wird geladen...",
"localeDe": "Deutsche",
@ -95,6 +97,7 @@
"newBulletinLabel": "Neue Meldung",
"newConnectionPaneTitle": "Neue Verbindung",
"newGroupBtn": "Neue Gruppe anlegen",
"newPassword": "",
"newProfile": "Neues Profil",
"noPasswordWarning": "Wenn für dieses Konto kein Passwort verwendet wird, bedeutet dies, dass alle lokal gespeicherten Daten nicht verschlüsselt werden.",
"password": "Passwort",
@ -116,6 +119,7 @@
"puzzleGameBtn": "Puzzlespiel",
"radioNoPassword": "Unverschlüsselt (kein Passwort)",
"radioUsePassword": "Passwort",
"reallyLeaveThisGroupPrompt": "",
"rejectGroupBtn": "Ablehnen",
"saveBtn": "Speichern",
"savePeerHistory": "Peer-Verlauf speichern",
@ -153,6 +157,7 @@
"versionTor": "Version %1 mit tor %2",
"viewGroupMembershipTooltip": "Gruppenmitgliedschaft anzeigen",
"viewServerInfo": "",
"yesLeave": "",
"yourDisplayName": "Ihr Anzeigename",
"yourProfiles": "Ihre Profile",
"yourServers": "Ihre Server",

View File

@ -75,10 +75,12 @@
"invitation": "Invitation",
"invitationLabel": "Invitation",
"inviteBtn": "Invite",
"inviteToGroup": "You have been invited to join a group:",
"inviteToGroupLabel": "Invite to group",
"joinGroup": "Join group",
"joinGroupTab": "Join a group",
"largeTextLabel": "Large",
"leaveGroup": "Leave This Conversation",
"listsBtn": "Lists",
"loadingTor": "Loading tor...",
"localeDe": "Deutsche",
@ -95,6 +97,7 @@
"newBulletinLabel": "New Bulletin",
"newConnectionPaneTitle": "New Connection",
"newGroupBtn": "Create new group",
"newPassword": "New Password",
"newProfile": "New Profile",
"noPasswordWarning": "Not using a password on this account means that all data stored locally will not be encrypted",
"password": "Password",
@ -103,7 +106,7 @@
"passwordChangeError": "Error changing password: Supplied password rejected",
"passwordErrorEmpty": "Password cannot be empty",
"passwordErrorMatch": "Passwords do not match",
"pasteAddressToAddContact": "Paste a cwtch address here to add a new contact.",
"pasteAddressToAddContact": "Paste a cwtch address, invitation or key bundle here to add a new conversation",
"peerAddress": "Address",
"peerBlockedMessage": "Peer is blocked",
"peerName": "Name",
@ -116,6 +119,7 @@
"puzzleGameBtn": "Puzzle Game",
"radioNoPassword": "Unencrypted (No password)",
"radioUsePassword": "Password",
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
"rejectGroupBtn": "Reject",
"saveBtn": "Save",
"savePeerHistory": "Save Peer History",
@ -137,12 +141,12 @@
"successfullAddedContact": "Successfully added ",
"themeDark": "Dark",
"themeLight": "Light",
"titleManageContacts": "Manage Contacts",
"titleManageContacts": "Conversations",
"titleManageProfiles": "Manage Cwtch Profiles",
"titleManageServers": "Manage Servers",
"titlePlaceholder": "title...",
"todoPlaceholder": "Todo...",
"tooltipAddContact": "Add a new contact",
"tooltipAddContact": "Add a new contact or conversation",
"tooltipOpenSettings": "Open the settings pane",
"tooltipUnlockProfiles": "Unlock encrypted profiles by entering their password.",
"unblockBtn": "Unblock Peer",
@ -153,6 +157,7 @@
"versionTor": "Version %1 with tor %2",
"viewGroupMembershipTooltip": "View Group Membership",
"viewServerInfo": "Server Info",
"yesLeave": "Yes, Leave This Conversation",
"yourDisplayName": "Your Display Name",
"yourProfiles": "Your Profiles",
"yourServers": "Your Servers",

View File

@ -75,10 +75,12 @@
"invitation": "Invitación",
"invitationLabel": "Invitación",
"inviteBtn": "Invitar",
"inviteToGroup": "",
"inviteToGroupLabel": "Invitar al grupo",
"joinGroup": "Únete al grupo",
"joinGroupTab": "Únete a un grupo",
"largeTextLabel": "Grande",
"leaveGroup": "",
"listsBtn": "Listas",
"loadingTor": "Cargando tor...",
"localeDe": "Alemán",
@ -95,6 +97,7 @@
"newBulletinLabel": "Nuevo Boletín",
"newConnectionPaneTitle": "Nueva conexión",
"newGroupBtn": "Crear un nuevo grupo de chat",
"newPassword": "",
"newProfile": "Nuevo perfil",
"noPasswordWarning": "No usar una contraseña para esta cuenta significa que los datos almacenados localmente no serán encriptados",
"password": "Contraseña",
@ -116,6 +119,7 @@
"puzzleGameBtn": "Juego de rompecabezas",
"radioNoPassword": "Sin cifrado (sin contraseña)",
"radioUsePassword": "Contraseña",
"reallyLeaveThisGroupPrompt": "",
"rejectGroupBtn": "Rechazar",
"saveBtn": "Guardar",
"savePeerHistory": "Guardar el historial con contacto",
@ -153,6 +157,7 @@
"versionTor": "Versión %1 con tor %2",
"viewGroupMembershipTooltip": "Ver membresía del grupo",
"viewServerInfo": "Información del servidor",
"yesLeave": "",
"yourDisplayName": "Tu nombre de usuario",
"yourProfiles": "Tus perfiles",
"yourServers": "Tus servidores",

View File

@ -75,10 +75,12 @@
"invitation": "",
"invitationLabel": "Invitation",
"inviteBtn": "Invitation",
"inviteToGroup": "",
"inviteToGroupLabel": "Inviter quelqu'un",
"joinGroup": "",
"joinGroupTab": "",
"largeTextLabel": "Large",
"leaveGroup": "",
"listsBtn": "Listes",
"loadingTor": "",
"localeDe": "",
@ -95,6 +97,7 @@
"newBulletinLabel": "Nouveau bulletin",
"newConnectionPaneTitle": "",
"newGroupBtn": "Créer un nouveau groupe",
"newPassword": "",
"newProfile": "",
"noPasswordWarning": "",
"password": "",
@ -116,6 +119,7 @@
"puzzleGameBtn": "Puzzle",
"radioNoPassword": "",
"radioUsePassword": "",
"reallyLeaveThisGroupPrompt": "",
"rejectGroupBtn": "Refuser",
"saveBtn": "Sauvegarder",
"savePeerHistory": "",
@ -153,6 +157,7 @@
"versionTor": "",
"viewGroupMembershipTooltip": "",
"viewServerInfo": "",
"yesLeave": "",
"yourDisplayName": "",
"yourProfiles": "",
"yourServers": "",

View File

@ -75,10 +75,12 @@
"invitation": "Invito",
"invitationLabel": "Invito",
"inviteBtn": "Invitare",
"inviteToGroup": "",
"inviteToGroupLabel": "Invitare nel gruppo",
"joinGroup": "Unisciti al gruppo",
"joinGroupTab": "Unisciti a un gruppo",
"largeTextLabel": "Grande",
"leaveGroup": "",
"listsBtn": "Liste",
"loadingTor": "Caricamento di tor...",
"localeDe": "Tedesco",
@ -95,6 +97,7 @@
"newBulletinLabel": "Nuovo bollettino",
"newConnectionPaneTitle": "Nuova connessione",
"newGroupBtn": "Crea un nuovo gruppo",
"newPassword": "",
"newProfile": "Nuovo profilo",
"noPasswordWarning": "Non utilizzare una password su questo account significa che tutti i dati archiviati localmente non verranno criptati",
"password": "Password",
@ -116,6 +119,7 @@
"puzzleGameBtn": "Gioco di puzzle",
"radioNoPassword": "Non criptato (senza password)",
"radioUsePassword": "Password",
"reallyLeaveThisGroupPrompt": "",
"rejectGroupBtn": "Rifiuta",
"saveBtn": "Salva",
"savePeerHistory": "Salva cronologia peer",
@ -153,6 +157,7 @@
"versionTor": "Versione %1 con tor %2",
"viewGroupMembershipTooltip": "Visualizza i membri del gruppo",
"viewServerInfo": "Informazioni sul server",
"yesLeave": "",
"yourDisplayName": "Il tuo nome visualizzato",
"yourProfiles": "I tuoi profili",
"yourServers": "I tuoi server",

View File

@ -75,10 +75,12 @@
"invitation": "",
"invitationLabel": "Convite",
"inviteBtn": "Convidar",
"inviteToGroup": "",
"inviteToGroupLabel": "Convidar ao grupo",
"joinGroup": "",
"joinGroupTab": "",
"largeTextLabel": "Grande",
"leaveGroup": "",
"listsBtn": "Listas",
"loadingTor": "",
"localeDe": "",
@ -95,6 +97,7 @@
"newBulletinLabel": "Novo Boletim",
"newConnectionPaneTitle": "",
"newGroupBtn": "Criar novo grupo",
"newPassword": "",
"newProfile": "",
"noPasswordWarning": "",
"password": "",
@ -116,6 +119,7 @@
"puzzleGameBtn": "Jogo de Adivinhação",
"radioNoPassword": "",
"radioUsePassword": "",
"reallyLeaveThisGroupPrompt": "",
"rejectGroupBtn": "Recusar",
"saveBtn": "Salvar",
"savePeerHistory": "",
@ -153,6 +157,7 @@
"versionTor": "",
"viewGroupMembershipTooltip": "",
"viewServerInfo": "",
"yesLeave": "",
"yourDisplayName": "",
"yourProfiles": "",
"yourServers": "",

View File

@ -1,4 +1,5 @@
import 'package:cwtch/notification_manager.dart';
import 'package:cwtch/widgets/rightshiftfixer.dart';
import 'package:flutter/foundation.dart';
import 'package:cwtch/cwtch/ffi.dart';
import 'package:cwtch/cwtch/gomobile.dart';
@ -42,7 +43,6 @@ class FlwtchState extends State<Flwtch> {
String selectedConversation = "";
var columns = [1]; // default or 'single column' mode
//var columns = [1, 1, 2];
late AppModel appStatus;
late ProfileListState profs;
@override
@ -68,8 +68,6 @@ class FlwtchState extends State<Flwtch> {
cwtchInit = true;
});
});
appStatus = AppModel(cwtch: cwtch);
}
ChangeNotifierProvider<TorStatus> getTorStatusProvider() => ChangeNotifierProvider.value(value: globalTorStatus);
@ -101,7 +99,7 @@ class FlwtchState extends State<Flwtch> {
theme: mkThemeData(settings),
// from dan: home: cwtchInit == true ? ProfileMgrView(cwtch) : SplashView(),
// from erinn: home: columns.length == 3 ? TripleColumnView() : ProfileMgrView(),
home: cwtchInit == true ? (columns.length == 3 ? TripleColumnView() : ProfileMgrView()) : SplashView(),
home: cwtchInit == true ? (columns.length == 3 ? TripleColumnView() : ShiftRightFixer(child: ProfileMgrView())) : SplashView(),
),
);
},

View File

@ -190,9 +190,16 @@ class ProfileInfoState extends ChangeNotifier {
// TODO Keys...
return ServerInfoState(onion: server["onion"], status: server["status"]);
}));
notifyListeners();
}
}
//
void updateServerStatusCache(String server, String status) {
this._servers.updateServerCache(server, status);
notifyListeners();
}
// Getters and Setters for Online Status
bool get isOnline => this._online;
set isOnline(bool newValue) {
@ -253,7 +260,7 @@ class ContactInfoState extends ChangeNotifier {
// todo: a nicer way to model contacts, groups and other "entities"
late bool _isGroup;
late String _server;
String? _server;
ContactInfoState(
this.profileOnion,
@ -268,7 +275,7 @@ class ContactInfoState extends ChangeNotifier {
numMessages = 0,
numUnread = 0,
lastMessageTime,
server = "",
server,
}) {
this._nickname = nickname;
this._isGroup = isGroup;
@ -470,37 +477,3 @@ class MessageState extends ChangeNotifier {
});
}
}
/////////////
/// ACN ///
/////////////
class AppModel {
final Cwtch cwtch;
AppModel({required this.cwtch});
Stream<String> contactEvents() async* {
while (true) {
String event = await cwtch.ContactEvents();
if (event != "") {
print(event);
yield event;
} else {
print("TEST TEST FAIL TEST FAIL 123");
await Future.delayed(Duration(seconds: 1));
}
}
}
Stream<String> torStatus() async* {
while (true) {
String event = await cwtch.ACNEvents();
if (event != "") {
yield event;
} else {
print("TOR TEST TEST FAIL TEST FAIL 123");
await Future.delayed(Duration(seconds: 1));
}
}
}
}

View File

@ -14,6 +14,16 @@ class ServerListState extends ChangeNotifier {
return idx >= 0 ? _servers[idx] : null;
}
void updateServerCache(String onion, String status) {
int idx = _servers.indexWhere((element) => element.onion == onion);
if (idx >= 0) {
_servers[idx] = ServerInfoState(onion: onion, status: status);
} else {
print("Tried to update server cache without a starting state...this is probably an error");
}
notifyListeners();
}
List<ServerInfoState> get servers => _servers.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier
}

View File

@ -34,7 +34,7 @@ class _AddContactViewState extends State<AddContactView> {
@override
Widget build(BuildContext context) {
// if we haven't picked a server yet, pick the first one in the list...
if (server.isEmpty) {
if (server.isEmpty && Provider.of<ProfileInfoState>(context).serverList.servers.isNotEmpty) {
server = Provider.of<ProfileInfoState>(context).serverList.servers.first.onion;
}

View File

@ -47,7 +47,7 @@ class _ContactsViewState extends State<ContactsView> {
width: 10,
),
Expanded(
child: Text("%1 » %2".replaceAll("%1", Provider.of<ProfileInfoState>(context).nickname).replaceAll("%2", "Contacts"),
child: Text("%1 » %2".replaceAll("%1", Provider.of<ProfileInfoState>(context).nickname).replaceAll("%2", AppLocalizations.of(context)!.titleManageContacts),
overflow: TextOverflow.ellipsis, style: TextStyle(color: Provider.of<Settings>(context).current().mainTextColor()))), //todo
])),
actions: [

View File

@ -31,7 +31,7 @@ class _DoubleColumnViewState extends State<DoubleColumnView> {
: //dev
MultiProvider(providers: [
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context)),
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context).contactList.getContact(flwtch.selectedConversation)),
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context).contactList.getContact(flwtch.selectedConversation)!),
], child: Container(child: MessageView())),
),
],

View File

@ -7,6 +7,7 @@ import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../main.dart';
import '../config.dart';
/// Global Settings View provides access to modify all the Globally Relevant Settings including Locale, Theme and Experiments.
class GlobalSettingsView extends StatefulWidget {
@ -156,7 +157,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
height: 128,
)),
applicationName: "Cwtch (Flutter UI)",
applicationVersion: AppLocalizations.of(context)!.version.replaceAll("%1", constructVersionString(Provider.of<Settings>(context).packageInfo)),
applicationVersion: AppLocalizations.of(context)!.versionBuilddate.replaceAll("%1", EnvironmentConfig.BUILD_VER).replaceAll("%2", EnvironmentConfig.BUILD_DATE),
applicationLegalese: '\u{a9} 2021 Open Privacy Research Society',
),
]))));

View File

@ -141,7 +141,7 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
showAlertDialog(context);
},
icon: Icon(Icons.delete),
label: Text(AppLocalizations.of(context)!.deleteBtn),
label: Text(AppLocalizations.of(context)!.leaveGroup),
))
])
])))));
@ -166,7 +166,7 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
);
Widget continueButton = TextButton(
style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))),
child: Text(AppLocalizations.of(context)!.deleteProfileConfirmBtn),
child: Text(AppLocalizations.of(context)!.yesLeave),
onPressed: () {
var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
var handle = Provider.of<ContactInfoState>(context, listen: false).onion;
@ -179,7 +179,7 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text(AppLocalizations.of(context)!.deleteProfileConfirmBtn),
title: Text(AppLocalizations.of(context)!.reallyLeaveThisGroupPrompt),
actions: [
cancelButton,
continueButton,

View File

@ -93,7 +93,8 @@ class _ContactRowState extends State<ContactRow> {
MaterialPageRoute<void>(
builder: (BuildContext builderContext) {
// assert we have an actual profile...
var profile = Provider.of<FlwtchState>(builderContext, listen: false).profs.getProfile(profileOnion)!;
// We need to listen for updates to the profile in order to update things like invitation message bubbles.
var profile = Provider.of<FlwtchState>(builderContext).profs.getProfile(profileOnion)!;
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: profile),

View File

@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
import '../main.dart';
import '../model.dart';
import 'package:intl/intl.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../settings.dart';
@ -34,11 +35,17 @@ class InvitationBubbleState extends State<InvitationBubble> {
prettyDate = DateFormat.yMd().add_jm().format(Provider.of<MessageState>(context).timestamp);
}
// If the sender is not us, then we want to give them a nickname...
var senderDisplayStr = "";
if (Provider.of<MessageState>(context).senderOnion != null) {
var contact = Provider.of<ProfileInfoState>(context).contactList.getContact(Provider.of<MessageState>(context).senderOnion);
senderDisplayStr = contact!.nickname;
if (!fromMe && Provider.of<MessageState>(context).senderOnion != null) {
ContactInfoState? contact = Provider.of<ProfileInfoState>(context).contactList.getContact(Provider.of<MessageState>(context).senderOnion);
if (contact != null) {
senderDisplayStr = contact.nickname;
} else {
senderDisplayStr = Provider.of<MessageState>(context).senderOnion;
}
}
var wdgSender = Center(
widthFactor: 1,
child: SelectableText(senderDisplayStr + '\u202F',
@ -50,9 +57,8 @@ class InvitationBubbleState extends State<InvitationBubble> {
//todo: get group name?
messageStr = "You sent an invitation for " + (isGroup ? "a group" : Provider.of<MessageState>(context).message ?? "");
} else {
messageStr = (isGroup ? "You have been invited to join " + (Provider.of<MessageState>(context).inviteNick ?? "") : "This is a contact suggestion for:") +
"\n" +
(Provider.of<MessageState>(context).inviteTarget ?? "");
String joinGroup = AppLocalizations.of(context)!.inviteToGroup;
messageStr = (isGroup ? joinGroup + (Provider.of<MessageState>(context).inviteNick ?? "") : "This is a contact suggestion for:") + "\n" + (Provider.of<MessageState>(context).inviteTarget ?? "");
}
var wdgMessage = Center(
widthFactor: 1,
@ -79,9 +85,13 @@ class InvitationBubbleState extends State<InvitationBubble> {
textAlign: fromMe ? TextAlign.right : TextAlign.left),
!fromMe
? SizedBox(width: 1, height: 1)
: Provider.of<MessageState>(context).ackd
? Icon(Icons.check_circle_outline, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 12)
: Icon(Icons.hourglass_bottom_outlined, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 12)
: Padding(
padding: EdgeInsets.all(1.0),
child: Provider.of<MessageState>(context).ackd == true
? Icon(Icons.check_circle_outline, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16)
: (Provider.of<MessageState>(context).error == true
? Icon(Icons.error_outline, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16)
: Icon(Icons.hourglass_bottom_outlined, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16)))
],
));
} else if (isAccepted) {

View File

@ -1,3 +1,4 @@
import 'package:cwtch/widgets/malformedbubble.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../model.dart';
@ -63,23 +64,32 @@ class MessageBubbleState extends State<MessageBubble> {
textAlign: fromMe ? TextAlign.right : TextAlign.left),
!fromMe
? SizedBox(width: 1, height: 1)
: Provider.of<MessageState>(context).ackd == true
? Icon(Icons.check_circle_outline, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 12)
: (Provider.of<MessageState>(context).error == true
? Icon(Icons.error_outline, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 12)
: Icon(Icons.hourglass_bottom_outlined, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 12))
: Padding(
padding: EdgeInsets.all(1.0),
child: Provider.of<MessageState>(context).ackd == true
? Icon(Icons.check_circle_outline, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16)
: (Provider.of<MessageState>(context).error == true
? Icon(Icons.error_outline, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16)
: Icon(Icons.hourglass_bottom_outlined, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16)))
],
));
var error = Provider.of<MessageState>(context).error;
return LayoutBuilder(builder: (context, constraints) {
//print(constraints.toString()+", "+constraints.maxWidth.toString());
return RepaintBoundary(
child: Container(
child: Container(
decoration: BoxDecoration(
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor() : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor(),
color: error
? malformedColor
: (fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor() : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor()),
border: Border.all(
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor() : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor(), width: 1),
color: error
? malformedColor
: (fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor() : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor()),
width: 1),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(borderRadiousEh),
topRight: Radius.circular(borderRadiousEh),

View File

@ -40,6 +40,7 @@ class _MessageListState extends State<MessageList> {
create: (x) => MessageState(
context: itemBuilderContext,
profileOnion: Provider.of<ProfileInfoState>(outerContext, listen: false).onion,
// We don't want to listen for updates to the contact handle...
contactHandle: Provider.of<ContactInfoState>(x, listen: false).onion,
messageIndex: trueIndex,
),

View File

@ -39,7 +39,9 @@ class _CwtchTextFieldState extends State<CwtchPasswordField> {
decoration: InputDecoration(
suffixIcon: IconButton(
onPressed: () {
obscureText = !obscureText;
setState(() {
obscureText = !obscureText;
});
},
icon: Icon((obscureText ? Icons.remove_red_eye : Icons.remove_red_eye_outlined), semanticLabel: label),
tooltip: label,

View File

@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// From https://github.com/flutter/flutter/issues/75675#issuecomment-846601115
// necessary to fix bug in flutter engine on Windows.
// todo: hopefully we can remove this soon
class ShiftRightFixer extends StatefulWidget {
ShiftRightFixer({required this.child});
final Widget child;
@override
State<StatefulWidget> createState() => _ShiftRightFixerState();
}
class _ShiftRightFixerState extends State<ShiftRightFixer> {
final FocusNode focus = FocusNode(skipTraversal: true, canRequestFocus: false);
@override
Widget build(BuildContext context) {
return Focus(
focusNode: focus,
onKey: (_, RawKeyEvent event) {
return event.physicalKey == PhysicalKeyboardKey.shiftRight ? KeyEventResult.handled : KeyEventResult.ignored;
},
child: widget.child,
);
}
}

View File

@ -1,27 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../main.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class TorStatusLabel extends StatefulWidget {
@override
_TorStatusState createState() => _TorStatusState();
}
class _TorStatusState extends State<TorStatusLabel> {
String status = "";
@override
Widget build(BuildContext context) {
return Builder(
builder: (context2) => StreamBuilder<String>(
stream: Provider.of<FlwtchState>(context).appStatus.torStatus(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
return Text(
snapshot.hasData ? snapshot.data! : AppLocalizations.of(context)!.loadingTor,
style: Theme.of(context).textTheme.headline4,
);
},
));
}
}

View File

@ -14,7 +14,7 @@ packages:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.1"
async:
dependency: transitive
description:
@ -77,7 +77,7 @@ packages:
name: dbus
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0"
version: "0.5.1"
desktop_notifications:
dependency: "direct main"
description:
@ -142,7 +142,7 @@ packages:
name: glob
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
version: "1.2.0"
http:
dependency: transitive
description:
@ -199,6 +199,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
node_interop:
dependency: transitive
description:
name: node_interop
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
node_io:
dependency: transitive
description:
name: node_io
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
package_info_plus:
dependency: "direct main"
description: