merge
continuous-integration/drone/pr Build is passing Details

This commit is contained in:
erinn 2021-06-11 14:29:18 -07:00
commit 37996b5d96
50 changed files with 471 additions and 176 deletions

View File

@ -59,7 +59,8 @@ steps:
- cp linux/cwtch.desktop deploy/linux - cp linux/cwtch.desktop deploy/linux
- cp linux/cwtch.png deploy/linux - cp linux/cwtch.png deploy/linux
- cp linux/libCwtch.so deploy/linux/lib/ - cp linux/libCwtch.so deploy/linux/lib/
- cp /sdks/flutter/bin/cache/artifacts/engine/linux-x64/icudtl.dat deploy/linux # should not be needed, should be in data/flutter_assets and work from there
#- cp /sdks/flutter/bin/cache/artifacts/engine/linux-x64/icudtl.dat deploy/linux
- cp tor deploy/linux - cp tor deploy/linux
- cd deploy - cd deploy
- mv linux cwtch - mv linux cwtch
@ -230,6 +231,12 @@ steps:
- mkdir deploy - mkdir deploy
- move build\\windows\\runner\\Release $Env:builddir - move build\\windows\\runner\\Release $Env:builddir
- copy windows\libCwtch.dll $Env:builddir - copy windows\libCwtch.dll $Env:builddir
# flutter hasn't worked out it's packaging of required dll's so we have to resort to this manual nonsense
# https://github.com/google/flutter-desktop-embedding/issues/587
# https://github.com/flutter/flutter/issues/53167
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\vcruntime140.dll $Env:builddir
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\vcruntime140_1.dll $Env:builddir
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\msvcp140.dll $Env:builddir
- powershell -command "Expand-Archive -Path tor.zip -DestinationPath $Env:builddir\Tor" - powershell -command "Expand-Archive -Path tor.zip -DestinationPath $Env:builddir\Tor"
- powershell -command "Compress-Archive -Path $Env:builddir -DestinationPath $Env:zip" - powershell -command "Compress-Archive -Path $Env:builddir -DestinationPath $Env:zip"
- powershell -command "(Get-FileHash *.zip -Algorithm sha512).Hash" > $Env:sha - powershell -command "(Get-FileHash *.zip -Algorithm sha512).Hash" > $Env:sha

View File

@ -1,6 +1,6 @@
# flwtch # flwtch
A new Flutter application. A Flutter based Cwtch UI
## Getting Started ## Getting Started
@ -8,13 +8,19 @@ click the play button in android studio
### Linux ### Linux
- libCwtch-go: the result of `make linux`, `libCwtch.so` should be in the link path - libCwtch-go: required to be on the link path (linux/cwtch.destktop demonstrates with `env LD_LIBRARY_PATH=./lib/` on the front of the comman)
- fetch-libcwtch-go.sh will fetch a prebuilt version
- or compile from source from libcwtch-go with `make linux`
- `tor` should be in the PATH - `tor` should be in the PATH
### Windows ### Windows
- libCwtch-go: the result of `make windows`, `libCwtch.dll` should be placed in the source root - run `fetch-libcwtch-go.ps1` to get `libCwtch.dll` which is required to run
- tor is bundled in `windors/Tor` - run `fetch-tor-win.ps1` to fetch Tor for windows
#### Issues
- Flutter engine has a [known bug](https://github.com/flutter/flutter/issues/75675) around the Right Shift key being sticky. We have implemented the mostly work around, but until it is fixed, right shift occasionally acts permenent. If this happens, just tap left shift and it will reset
## l10n ## l10n

View File

@ -17,10 +17,10 @@
style="enable-background:new 0 0 24 24;" style="enable-background:new 0 0 24 24;"
xml:space="preserve" xml:space="preserve"
sodipodi:docname="negative_heart_24px.svg" sodipodi:docname="negative_heart_24px.svg"
inkscape:export-filename="/home/sarah/AndroidStudioProjects/flutter_app/assets/core/negative_heart_512px.png" inkscape:export-filename="/home/sarah/PARA/projects/cwtch/assets/core/negative_heart_256px.png"
inkscape:export-xdpi="4096" inkscape:export-xdpi="1024"
inkscape:export-ydpi="4096" inkscape:export-ydpi="1024"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"><metadata inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><metadata
id="metadata14"><rdf:RDF><cc:Work id="metadata14"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
@ -34,14 +34,14 @@
inkscape:pageopacity="0" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:window-width="1920" inkscape:window-width="1920"
inkscape:window-height="1015" inkscape:window-height="1020"
id="namedview10" id="namedview10"
showgrid="false" showgrid="false"
inkscape:zoom="9.8333333" inkscape:zoom="9.8333333"
inkscape:cx="12" inkscape:cx="-5.8983051"
inkscape:cy="14.687942" inkscape:cy="14.281162"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="0" inkscape:window-y="31"
inkscape:window-maximized="1" inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" /> inkscape:current-layer="Layer_1" />
<style <style

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
assets/fonts/CwtchIcons.ttf Normal file

Binary file not shown.

View File

@ -207,6 +207,7 @@ class CwtchNotifier {
break; break;
case "AcceptGroupInvite": case "AcceptGroupInvite":
print("accept group invite: $data"); print("accept group invite: $data");
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.isInvitation = false; profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.isInvitation = false;
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now()); profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now());
break; break;

109
lib/cwtch_icons_icons.dart Normal file
View File

@ -0,0 +1,109 @@
/// Flutter icons MyFlutterApp
/// Copyright (C) 2021 by original authors @ fluttericon.com, fontello.com
/// This font was generated by FlutterIcon.com, which is derived from Fontello.
///
/// To use this font, place it in your fonts/ directory and include the
/// following in your pubspec.yaml
///
/// flutter:
/// fonts:
/// - family: CwtchIcons
/// fonts:
/// - asset: assets/fonts/CwtchIcons.ttf
///
///
///
import 'package:flutter/widgets.dart';
class CwtchIcons {
CwtchIcons._();
static const _kFontFam = 'CwtchIcons';
static const String? _kFontPkg = null;
static const IconData arrow_back_24px = IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData attach_file_24px = IconData(0xe801, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData block_peer = IconData(0xe802, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData block_unknown = IconData(0xe803, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData block_24px = IconData(0xe804, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData brightness_5_24px = IconData(0xe805, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData camera_alt_24px = IconData(0xe806, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData change_language = IconData(0xe807, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData change_theme = IconData(0xe808, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData chat_bubble_empty_24px = IconData(0xe809, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData chat_bubble_24px = IconData(0xe80a, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData chat_seetings_24px = IconData(0xe80b, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData check_24px = IconData(0xe80c, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData chevron_left_24px = IconData(0xe80d, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData clear_24px = IconData(0xe80e, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData content_copy_24px = IconData(0xe80f, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData create_group = IconData(0xe810, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData cwtch_knott = IconData(0xe811, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData dark_mode_24px = IconData(0xe812, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData delete_24px = IconData(0xe813, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData dns_24px = IconData(0xe814, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData drag_indicator_24px = IconData(0xe815, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData edit_24px = IconData(0xe816, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData enable_experiments = IconData(0xe817, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData enable_groups = IconData(0xe818, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData eye_closed = IconData(0xe819, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData eye_open = IconData(0xe81a, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData favorite_24dp = IconData(0xe81b, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData favorite_black_24dp_broken = IconData(0xe81c, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData favorite_black_24dp_brokenhalf = IconData(0xe81d, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData favorite_black_24dp_malformed = IconData(0xe81e, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData favorite_black_24dp_sad = IconData(0xe81f, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData group_add_24px = IconData(0xe820, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData group_settings_24px = IconData(0xe821, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData groups_24px = IconData(0xe822, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData info_24px = IconData(0xe823, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData join_group = IconData(0xe824, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData list_black_24dp = IconData(0xe825, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData lock_open_24px = IconData(0xe826, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData lock_24px = IconData(0xe827, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData maps_ugc_24px = IconData(0xe828, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData menu_24px = IconData(0xe829, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData message_24px = IconData(0xe82a, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData mood_24px = IconData(0xe82b, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData more_vert_24px = IconData(0xe82c, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData negative_heart_24px = IconData(0xe82d, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData onion_off = IconData(0xe82e, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData onion_on = IconData(0xe82f, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData onion_waiting = IconData(0xe830, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData peer_history = IconData(0xe831, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData peer_settings_24px = IconData(0xe832, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData person_add_alt_1_24px = IconData(0xe833, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData person_add_24px = IconData(0xe834, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData person_24px = IconData(0xe835, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData push_pin_black_24dp = IconData(0xe836, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData push_pin_24px = IconData(0xe837, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData search_24px = IconData(0xe838, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData send_24px = IconData(0xe839, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData settings_24px = IconData(0xe83a, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData signal_cellular_4_bar_24px = IconData(0xe83b, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData signal_cellular_alt_24px = IconData(0xe83c, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData signal_cellular_connected_no_internet_4_bar_24px = IconData(0xe83d, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData signal_cellular_off_24px = IconData(0xe83e, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData swap_horiz_24px = IconData(0xe83f, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData sync_disabled_24px = IconData(0xe840, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData sync_problem_24px = IconData(0xe841, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData sync_24px = IconData(0xe842, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData syncing_01 = IconData(0xe843, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData syncing_02 = IconData(0xe844, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData syncing_03 = IconData(0xe845, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData toggle_on_24px = IconData(0xe846, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData vpn_key_24px = IconData(0xe847, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData account_blocked = IconData(0xe848, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData account_circle_24px = IconData(0xe849, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData account_circle_24px_lines = IconData(0xe84a, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData account_circle_24px_lines_thin___blocked = IconData(0xe84b, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData account_circle_24px_user = IconData(0xe84c, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData add_circle_24px = IconData(0xe84d, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData add_group = IconData(0xe84e, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData add_peer = IconData(0xe84f, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData add_24px = IconData(0xe850, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData send_invite = IconData(0xe888, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData copy_address = IconData(0xe889, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData leave_group = IconData(0xe88a, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData leave_chat = IconData(0xe88b, fontFamily: _kFontFam, fontPackage: _kFontPkg);
}

View File

@ -1,5 +1,6 @@
{ {
"@@locale": "de", "@@locale": "de",
"accepted": "",
"acceptGroupBtn": "Annehmen", "acceptGroupBtn": "Annehmen",
"acceptGroupInviteLabel": "Möchtest Du die Einladung annehmen", "acceptGroupInviteLabel": "Möchtest Du die Einladung annehmen",
"acknowledgedLabel": "bestätigt", "acknowledgedLabel": "bestätigt",
@ -17,7 +18,9 @@
"builddate": "Aufgebaut auf: %2", "builddate": "Aufgebaut auf: %2",
"bulletinsBtn": "Meldungen", "bulletinsBtn": "Meldungen",
"chatBtn": "Chat", "chatBtn": "Chat",
"chatHistoryDefault": "",
"contactAlreadyExists": "", "contactAlreadyExists": "",
"contactSuggestion": "",
"conversationSettings": "", "conversationSettings": "",
"copiedClipboardNotification": "in die Zwischenablage kopiert", "copiedClipboardNotification": "in die Zwischenablage kopiert",
"copiedToClipboardNotification": "in die Zwischenablage kopiert", "copiedToClipboardNotification": "in die Zwischenablage kopiert",
@ -120,6 +123,7 @@
"radioNoPassword": "Unverschlüsselt (kein Passwort)", "radioNoPassword": "Unverschlüsselt (kein Passwort)",
"radioUsePassword": "Passwort", "radioUsePassword": "Passwort",
"reallyLeaveThisGroupPrompt": "", "reallyLeaveThisGroupPrompt": "",
"rejected": "",
"rejectGroupBtn": "Ablehnen", "rejectGroupBtn": "Ablehnen",
"saveBtn": "Speichern", "saveBtn": "Speichern",
"savePeerHistory": "Peer-Verlauf speichern", "savePeerHistory": "Peer-Verlauf speichern",
@ -127,6 +131,7 @@
"saveProfileBtn": "Profil speichern", "saveProfileBtn": "Profil speichern",
"search": "Suche...", "search": "Suche...",
"searchList": "", "searchList": "",
"sendAnInvitation": "",
"server": "Server", "server": "Server",
"serverConnectivityConnected": "Server verbunden", "serverConnectivityConnected": "Server verbunden",
"serverConnectivityDisconnected": "Server getrennt", "serverConnectivityDisconnected": "Server getrennt",

View File

@ -1,5 +1,6 @@
{ {
"@@locale": "en", "@@locale": "en",
"accepted": "Accepted!",
"acceptGroupBtn": "Accept", "acceptGroupBtn": "Accept",
"acceptGroupInviteLabel": "Do you want to accept the invitation to", "acceptGroupInviteLabel": "Do you want to accept the invitation to",
"acknowledgedLabel": "Acknowledged", "acknowledgedLabel": "Acknowledged",
@ -17,7 +18,9 @@
"builddate": "Built on: %2", "builddate": "Built on: %2",
"bulletinsBtn": "Bulletins", "bulletinsBtn": "Bulletins",
"chatBtn": "Chat", "chatBtn": "Chat",
"chatHistoryDefault": "This conversation will be deleted when Cwtch is closed! Message history can be enabled per-conversation via the Settings menu in the upper right.",
"contactAlreadyExists": "Contact Already Exists", "contactAlreadyExists": "Contact Already Exists",
"contactSuggestion": "This is a contact suggestion for: ",
"conversationSettings": "Conversation Settings", "conversationSettings": "Conversation Settings",
"copiedClipboardNotification": "Copied to clipboard", "copiedClipboardNotification": "Copied to clipboard",
"copiedToClipboardNotification": "Copied to Clipboard", "copiedToClipboardNotification": "Copied to Clipboard",
@ -120,6 +123,7 @@
"radioNoPassword": "Unencrypted (No password)", "radioNoPassword": "Unencrypted (No password)",
"radioUsePassword": "Password", "radioUsePassword": "Password",
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.", "reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
"rejected": "Rejected!",
"rejectGroupBtn": "Reject", "rejectGroupBtn": "Reject",
"saveBtn": "Save", "saveBtn": "Save",
"savePeerHistory": "Save Peer History", "savePeerHistory": "Save Peer History",
@ -127,6 +131,7 @@
"saveProfileBtn": "Save Profile", "saveProfileBtn": "Save Profile",
"search": "Search...", "search": "Search...",
"searchList": "Search List", "searchList": "Search List",
"sendAnInvitation": "You sent an invitation for: ",
"server": "Server", "server": "Server",
"serverConnectivityConnected": "Server Connected", "serverConnectivityConnected": "Server Connected",
"serverConnectivityDisconnected": "Server Disconnected", "serverConnectivityDisconnected": "Server Disconnected",

View File

@ -1,5 +1,6 @@
{ {
"@@locale": "es", "@@locale": "es",
"accepted": "",
"acceptGroupBtn": "Aceptar", "acceptGroupBtn": "Aceptar",
"acceptGroupInviteLabel": "¿Quieres aceptar la invitación a ", "acceptGroupInviteLabel": "¿Quieres aceptar la invitación a ",
"acknowledgedLabel": "Reconocido", "acknowledgedLabel": "Reconocido",
@ -17,7 +18,9 @@
"builddate": "Basado en: %2", "builddate": "Basado en: %2",
"bulletinsBtn": "Boletines", "bulletinsBtn": "Boletines",
"chatBtn": "Chat", "chatBtn": "Chat",
"chatHistoryDefault": "",
"contactAlreadyExists": "", "contactAlreadyExists": "",
"contactSuggestion": "",
"conversationSettings": "", "conversationSettings": "",
"copiedClipboardNotification": "Copiado al portapapeles", "copiedClipboardNotification": "Copiado al portapapeles",
"copiedToClipboardNotification": "Copiado al portapapeles", "copiedToClipboardNotification": "Copiado al portapapeles",
@ -120,6 +123,7 @@
"radioNoPassword": "Sin cifrado (sin contraseña)", "radioNoPassword": "Sin cifrado (sin contraseña)",
"radioUsePassword": "Contraseña", "radioUsePassword": "Contraseña",
"reallyLeaveThisGroupPrompt": "", "reallyLeaveThisGroupPrompt": "",
"rejected": "",
"rejectGroupBtn": "Rechazar", "rejectGroupBtn": "Rechazar",
"saveBtn": "Guardar", "saveBtn": "Guardar",
"savePeerHistory": "Guardar el historial con contacto", "savePeerHistory": "Guardar el historial con contacto",
@ -127,6 +131,7 @@
"saveProfileBtn": "Guardar perfil", "saveProfileBtn": "Guardar perfil",
"search": "Búsqueda...", "search": "Búsqueda...",
"searchList": "Buscar en la lista", "searchList": "Buscar en la lista",
"sendAnInvitation": "",
"server": "Servidor", "server": "Servidor",
"serverConnectivityConnected": "Servidor conectado", "serverConnectivityConnected": "Servidor conectado",
"serverConnectivityDisconnected": "Servidor desconectado", "serverConnectivityDisconnected": "Servidor desconectado",

View File

@ -1,5 +1,6 @@
{ {
"@@locale": "fr", "@@locale": "fr",
"accepted": "",
"acceptGroupBtn": "Accepter", "acceptGroupBtn": "Accepter",
"acceptGroupInviteLabel": "Voulez-vous accepter l'invitation au groupe", "acceptGroupInviteLabel": "Voulez-vous accepter l'invitation au groupe",
"acknowledgedLabel": "Confirmé", "acknowledgedLabel": "Confirmé",
@ -17,7 +18,9 @@
"builddate": "", "builddate": "",
"bulletinsBtn": "Bulletins", "bulletinsBtn": "Bulletins",
"chatBtn": "Discuter", "chatBtn": "Discuter",
"chatHistoryDefault": "",
"contactAlreadyExists": "", "contactAlreadyExists": "",
"contactSuggestion": "",
"conversationSettings": "", "conversationSettings": "",
"copiedClipboardNotification": "Copié dans le presse-papier", "copiedClipboardNotification": "Copié dans le presse-papier",
"copiedToClipboardNotification": "Copié dans le presse-papier", "copiedToClipboardNotification": "Copié dans le presse-papier",
@ -120,6 +123,7 @@
"radioNoPassword": "", "radioNoPassword": "",
"radioUsePassword": "", "radioUsePassword": "",
"reallyLeaveThisGroupPrompt": "", "reallyLeaveThisGroupPrompt": "",
"rejected": "",
"rejectGroupBtn": "Refuser", "rejectGroupBtn": "Refuser",
"saveBtn": "Sauvegarder", "saveBtn": "Sauvegarder",
"savePeerHistory": "", "savePeerHistory": "",
@ -127,6 +131,7 @@
"saveProfileBtn": "", "saveProfileBtn": "",
"search": "", "search": "",
"searchList": "", "searchList": "",
"sendAnInvitation": "",
"server": "", "server": "",
"serverConnectivityConnected": "", "serverConnectivityConnected": "",
"serverConnectivityDisconnected": "", "serverConnectivityDisconnected": "",

View File

@ -1,5 +1,6 @@
{ {
"@@locale": "it", "@@locale": "it",
"accepted": "",
"acceptGroupBtn": "Accetta", "acceptGroupBtn": "Accetta",
"acceptGroupInviteLabel": "Vuoi accettare l'invito a", "acceptGroupInviteLabel": "Vuoi accettare l'invito a",
"acknowledgedLabel": "Riconosciuto", "acknowledgedLabel": "Riconosciuto",
@ -17,7 +18,9 @@
"builddate": "Costruito il: %2", "builddate": "Costruito il: %2",
"bulletinsBtn": "Bollettini", "bulletinsBtn": "Bollettini",
"chatBtn": "Chat", "chatBtn": "Chat",
"chatHistoryDefault": "",
"contactAlreadyExists": "", "contactAlreadyExists": "",
"contactSuggestion": "",
"conversationSettings": "", "conversationSettings": "",
"copiedClipboardNotification": "Copiato negli Appunti", "copiedClipboardNotification": "Copiato negli Appunti",
"copiedToClipboardNotification": "Copiato negli Appunti", "copiedToClipboardNotification": "Copiato negli Appunti",
@ -120,6 +123,7 @@
"radioNoPassword": "Non criptato (senza password)", "radioNoPassword": "Non criptato (senza password)",
"radioUsePassword": "Password", "radioUsePassword": "Password",
"reallyLeaveThisGroupPrompt": "", "reallyLeaveThisGroupPrompt": "",
"rejected": "",
"rejectGroupBtn": "Rifiuta", "rejectGroupBtn": "Rifiuta",
"saveBtn": "Salva", "saveBtn": "Salva",
"savePeerHistory": "Salva cronologia peer", "savePeerHistory": "Salva cronologia peer",
@ -127,6 +131,7 @@
"saveProfileBtn": "Salva il profilo", "saveProfileBtn": "Salva il profilo",
"search": "Ricerca...", "search": "Ricerca...",
"searchList": "Cerca nella lista", "searchList": "Cerca nella lista",
"sendAnInvitation": "",
"server": "Server", "server": "Server",
"serverConnectivityConnected": "Server connesso", "serverConnectivityConnected": "Server connesso",
"serverConnectivityDisconnected": "Server disconnesso", "serverConnectivityDisconnected": "Server disconnesso",

View File

@ -1,5 +1,6 @@
{ {
"@@locale": "pt", "@@locale": "pt",
"accepted": "",
"acceptGroupBtn": "Aceitar", "acceptGroupBtn": "Aceitar",
"acceptGroupInviteLabel": "Você quer aceitar o convite para", "acceptGroupInviteLabel": "Você quer aceitar o convite para",
"acknowledgedLabel": "Confirmada", "acknowledgedLabel": "Confirmada",
@ -17,7 +18,9 @@
"builddate": "", "builddate": "",
"bulletinsBtn": "Boletins", "bulletinsBtn": "Boletins",
"chatBtn": "Chat", "chatBtn": "Chat",
"chatHistoryDefault": "",
"contactAlreadyExists": "", "contactAlreadyExists": "",
"contactSuggestion": "",
"conversationSettings": "", "conversationSettings": "",
"copiedClipboardNotification": "Copiado", "copiedClipboardNotification": "Copiado",
"copiedToClipboardNotification": "Copiado", "copiedToClipboardNotification": "Copiado",
@ -120,6 +123,7 @@
"radioNoPassword": "", "radioNoPassword": "",
"radioUsePassword": "", "radioUsePassword": "",
"reallyLeaveThisGroupPrompt": "", "reallyLeaveThisGroupPrompt": "",
"rejected": "",
"rejectGroupBtn": "Recusar", "rejectGroupBtn": "Recusar",
"saveBtn": "Salvar", "saveBtn": "Salvar",
"savePeerHistory": "", "savePeerHistory": "",
@ -127,6 +131,7 @@
"saveProfileBtn": "", "saveProfileBtn": "",
"search": "", "search": "",
"searchList": "", "searchList": "",
"sendAnInvitation": "",
"server": "", "server": "",
"serverConnectivityConnected": "", "serverConnectivityConnected": "",
"serverConnectivityDisconnected": "", "serverConnectivityDisconnected": "",

View File

@ -64,13 +64,13 @@ class ContactListState extends ChangeNotifier {
bool get isFiltered => _filter != ""; bool get isFiltered => _filter != "";
String get filter => _filter; String get filter => _filter;
set filter(String newVal) { set filter(String newVal) {
_filter = newVal; _filter = newVal.toLowerCase();
notifyListeners(); notifyListeners();
} }
List<ContactInfoState> filteredList() { List<ContactInfoState> filteredList() {
if (!isFiltered) return contacts; if (!isFiltered) return contacts;
return _contacts.where((ContactInfoState c) => c.onion.contains(_filter) || (c.nickname.contains(_filter))).toList(); return _contacts.where((ContactInfoState c) => c.onion.toLowerCase().startsWith(_filter) || (c.nickname.toLowerCase().contains(_filter))).toList();
} }
void addAll(Iterable<ContactInfoState> newContacts) { void addAll(Iterable<ContactInfoState> newContacts) {
@ -382,7 +382,7 @@ class MessageState extends ChangeNotifier {
late String _inviteNick; late String _inviteNick;
late DateTime _timestamp; late DateTime _timestamp;
late String _senderOnion; late String _senderOnion;
late String _senderImage; String? _senderImage;
late String _signature = ""; late String _signature = "";
late bool _ackd = false; late bool _ackd = false;
late bool _error = false; late bool _error = false;
@ -404,10 +404,10 @@ class MessageState extends ChangeNotifier {
get timestamp => this._timestamp; get timestamp => this._timestamp;
bool get ackd => this._ackd; bool get ackd => this._ackd;
bool get error => this._error; bool get error => this._error;
get malformed => this._malformed; bool get malformed => this._malformed;
bool get loaded => this._loaded;
get senderOnion => this._senderOnion; get senderOnion => this._senderOnion;
get senderImage => this._senderImage; get senderImage => this._senderImage;
get loaded => this._loaded;
get signature => this._signature; get signature => this._signature;
get isInvite => this.overlay == 100 || this.overlay == 101; get isInvite => this.overlay == 100 || this.overlay == 101;
get inviteTarget => this._inviteTarget; get inviteTarget => this._inviteTarget;
@ -423,6 +423,16 @@ class MessageState extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
set malformed(bool newVal) {
this._malformed = newVal;
notifyListeners();
}
set loaded(bool newVal) {
this._loaded = newVal;
notifyListeners();
}
void tryLoad(BuildContext context) { void tryLoad(BuildContext context) {
Provider.of<FlwtchState>(context, listen: false).cwtch.GetMessage(profileOnion, contactHandle, messageIndex).then((jsonMessage) { Provider.of<FlwtchState>(context, listen: false).cwtch.GetMessage(profileOnion, contactHandle, messageIndex).then((jsonMessage) {
try { try {
@ -430,7 +440,6 @@ class MessageState extends ChangeNotifier {
dynamic messageWrapper = jsonDecode(jsonMessage); dynamic messageWrapper = jsonDecode(jsonMessage);
if (messageWrapper['Message'] == null || messageWrapper['Message'] == '' || messageWrapper['Message'] == '{}') { if (messageWrapper['Message'] == null || messageWrapper['Message'] == '' || messageWrapper['Message'] == '{}') {
this._senderOnion = profileOnion; this._senderOnion = profileOnion;
//todo: remove once sent group messages are prestored
Future.delayed(const Duration(milliseconds: 2), () { Future.delayed(const Duration(milliseconds: 2), () {
tryLoad(context); tryLoad(context);
}); });
@ -464,7 +473,7 @@ class MessageState extends ChangeNotifier {
} }
} }
this._loaded = true; this.loaded = true;
//update ackd and error last as they are changenotified //update ackd and error last as they are changenotified
this.ackd = messageWrapper['Acknowledged']; this.ackd = messageWrapper['Acknowledged'];
@ -472,7 +481,9 @@ class MessageState extends ChangeNotifier {
this.error = true; this.error = true;
} }
} catch (e) { } catch (e) {
this._malformed = true; this._overlay = -1;
this.loaded = true;
this.malformed = true;
} }
}); });
} }

View File

@ -1357,14 +1357,14 @@ ThemeData mkThemeData(Settings opaque) {
), ),
cardColor: opaque.current().backgroundMainColor(), cardColor: opaque.current().backgroundMainColor(),
appBarTheme: AppBarTheme( appBarTheme: AppBarTheme(
backgroundColor: opaque.current().backgroundPaneColor(), backgroundColor: opaque.current().backgroundPaneColor(),
titleTextStyle: TextStyle( titleTextStyle: TextStyle(
color: opaque.current().mainTextColor(), color: opaque.current().mainTextColor(),
), ),
actionsIconTheme: IconThemeData( actionsIconTheme: IconThemeData(
color: opaque.current().mainTextColor(), color: opaque.current().mainTextColor(),
), )),
), bottomNavigationBarTheme: BottomNavigationBarThemeData(type: BottomNavigationBarType.fixed, backgroundColor: opaque.current().backgroundHilightElementColor()),
textButtonTheme: TextButtonThemeData( textButtonTheme: TextButtonThemeData(
style: ButtonStyle( style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(opaque.current().defaultButtonColor()), backgroundColor: MaterialStateProperty.all(opaque.current().defaultButtonColor()),
@ -1383,6 +1383,8 @@ ThemeData mkThemeData(Settings opaque) {
)), )),
), ),
), ),
scrollbarTheme: ScrollbarThemeData(
isAlwaysShown: false, thumbColor: MaterialStateProperty.all(opaque.current().scrollbarActiveColor()), trackColor: MaterialStateProperty.all(opaque.current().scrollbarDefaultColor())),
tabBarTheme: TabBarTheme(indicator: UnderlineTabIndicator(borderSide: BorderSide(color: opaque.current().defaultButtonActiveColor()))), tabBarTheme: TabBarTheme(indicator: UnderlineTabIndicator(borderSide: BorderSide(color: opaque.current().defaultButtonActiveColor()))),
dialogTheme: DialogTheme( dialogTheme: DialogTheme(
backgroundColor: opaque.current().backgroundPaneColor(), backgroundColor: opaque.current().backgroundPaneColor(),

View File

@ -16,7 +16,8 @@ class Settings extends ChangeNotifier {
Locale locale; Locale locale;
late PackageInfo packageInfo; late PackageInfo packageInfo;
OpaqueThemeType theme; OpaqueThemeType theme;
late bool experimentsEnabled; // explicitly set experiments to false until told otherwise...
bool experimentsEnabled = false;
HashMap<String, bool> experiments = HashMap.identity(); HashMap<String, bool> experiments = HashMap.identity();
late bool blockUnknownConnections; late bool blockUnknownConnections;
@ -38,6 +39,18 @@ class Settings extends ChangeNotifier {
return theme; return theme;
} }
/// isExperimentEnabled can be used to safely check whether a particular
/// experiment is enabled
bool isExperimentEnabled(String experiment) {
if (this.experimentsEnabled) {
if (this.experiments.containsKey(experiment)) {
// We now know it cannot be null...
return this.experiments[experiment]! == true;
}
}
return false;
}
/// Called by the event bus. When new settings are loaded from a file the JSON will /// Called by the event bus. When new settings are loaded from a file the JSON will
/// be sent to the function and new settings will be instantiated based on the contents. /// be sent to the function and new settings will be instantiated based on the contents.
handleUpdate(dynamic settings) { handleUpdate(dynamic settings) {

View File

@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:cwtch/errorHandler.dart'; import 'package:cwtch/errorHandler.dart';
@ -49,8 +50,8 @@ class _AddContactViewState extends State<AddContactView> {
Widget _buildForm() { Widget _buildForm() {
ctrlrOnion.text = Provider.of<ProfileInfoState>(context).onion; ctrlrOnion.text = Provider.of<ProfileInfoState>(context).onion;
/// We display a different number of tabs dependening on the experiment setup /// We display a different number of tabs depending on the experiment setup
bool groupsEnabled = Provider.of<Settings>(context).experimentsEnabled && Provider.of<Settings>(context).experiments[TapirGroupsExperiment]!; bool groupsEnabled = Provider.of<Settings>(context).isExperimentEnabled(TapirGroupsExperiment);
return Consumer<ErrorHandler>(builder: (context, globalErrorHandler, child) { return Consumer<ErrorHandler>(builder: (context, globalErrorHandler, child) {
return DefaultTabController( return DefaultTabController(
length: groupsEnabled ? 2 : 1, length: groupsEnabled ? 2 : 1,
@ -80,7 +81,7 @@ class _AddContactViewState extends State<AddContactView> {
return TabBar( return TabBar(
tabs: [ tabs: [
Tab( Tab(
icon: Icon(Icons.person_add_rounded), icon: Icon(CwtchIcons.add_peer),
text: AppLocalizations.of(context)!.addPeer, text: AppLocalizations.of(context)!.addPeer,
), ),
], ],
@ -92,11 +93,11 @@ class _AddContactViewState extends State<AddContactView> {
return TabBar( return TabBar(
tabs: [ tabs: [
Tab( Tab(
icon: Icon(Icons.person_add_rounded), icon: Icon(CwtchIcons.add_peer),
text: AppLocalizations.of(context)!.tooltipAddContact, text: AppLocalizations.of(context)!.tooltipAddContact,
), ),
//Tab(icon: Icon(Icons.backup), text: AppLocalizations.of(context)!.titleManageServers), //Tab(icon: Icon(Icons.backup), text: AppLocalizations.of(context)!.titleManageServers),
Tab(icon: Icon(Icons.group), text: AppLocalizations.of(context)!.createGroup), Tab(icon: Icon(CwtchIcons.add_group), text: AppLocalizations.of(context)!.createGroup),
], ],
); );
} }
@ -118,7 +119,7 @@ class _AddContactViewState extends State<AddContactView> {
CwtchButtonTextField( CwtchButtonTextField(
controller: ctrlrOnion, controller: ctrlrOnion,
onPressed: _copyOnion, onPressed: _copyOnion,
icon: Icon(Icons.copy), icon: Icon(CwtchIcons.copy_address),
tooltip: AppLocalizations.of(context)!.copyBtn, tooltip: AppLocalizations.of(context)!.copyBtn,
), ),
SizedBox( SizedBox(

View File

@ -99,6 +99,7 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
), ),
CwtchTextField( CwtchTextField(
controller: ctrlrNick, controller: ctrlrNick,
autofocus: true,
labelText: AppLocalizations.of(context)!.yourDisplayName, labelText: AppLocalizations.of(context)!.yourDisplayName,
validator: (value) { validator: (value) {
if (value.isEmpty) { if (value.isEmpty) {
@ -167,7 +168,7 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
height: 20, height: 20,
), ),
])), ])),
CwtchLabel(label: AppLocalizations.of(context)!.password1Label), CwtchLabel(label: AppLocalizations.of(context)!.newPassword),
SizedBox( SizedBox(
height: 20, height: 20,
), ),

View File

@ -1,3 +1,4 @@
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cwtch/views/torstatusview.dart'; import 'package:cwtch/views/torstatusview.dart';
import 'package:cwtch/widgets/contactrow.dart'; import 'package:cwtch/widgets/contactrow.dart';
@ -66,7 +67,7 @@ class _ContactsViewState extends State<ContactsView> {
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
onPressed: _pushAddContact, onPressed: _pushAddContact,
tooltip: AppLocalizations.of(context)!.tooltipAddContact, tooltip: AppLocalizations.of(context)!.tooltipAddContact,
child: const Icon(Icons.person_add_sharp), child: const Icon(CwtchIcons.person_add_alt_1_24px),
), ),
body: showSearchBar || Provider.of<ContactListState>(context).isFiltered ? _buildFilterable() : _buildContactList()); body: showSearchBar || Provider.of<ContactListState>(context).isFiltered ? _buildFilterable() : _buildContactList());
} }

View File

@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:cwtch/cwtch_icons_icons.dart';
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';
import 'package:cwtch/opaque.dart'; import 'package:cwtch/opaque.dart';
@ -46,7 +47,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
child: Column(children: [ child: Column(children: [
ListTile( ListTile(
title: Text(AppLocalizations.of(context)!.settingLanguage, style: TextStyle(color: settings.current().mainTextColor())), title: Text(AppLocalizations.of(context)!.settingLanguage, style: TextStyle(color: settings.current().mainTextColor())),
leading: Icon(Icons.language, color: settings.current().mainTextColor()), leading: Icon(CwtchIcons.change_language, color: settings.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) {
@ -74,7 +75,9 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
// Save Settings... // Save Settings...
saveSettings(context); saveSettings(context);
}, },
secondary: Icon(Icons.lightbulb_outline, color: settings.current().mainTextColor()), activeTrackColor: settings.theme.defaultButtonActiveColor(),
inactiveTrackColor: settings.theme.defaultButtonDisabledColor(),
secondary: Icon(CwtchIcons.change_theme, color: settings.current().mainTextColor()),
), ),
ListTile( ListTile(
title: Text(/*AppLocalizations.of(context)!.settingLanguage*/ "UI Columns", style: TextStyle(color: settings.current().mainTextColor())), title: Text(/*AppLocalizations.of(context)!.settingLanguage*/ "UI Columns", style: TextStyle(color: settings.current().mainTextColor())),
@ -111,7 +114,9 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
// Save Settings... // Save Settings...
saveSettings(context); saveSettings(context);
}, },
secondary: Icon(Icons.app_blocking, color: settings.current().mainTextColor()), activeTrackColor: settings.theme.defaultButtonActiveColor(),
inactiveTrackColor: settings.theme.defaultButtonDisabledColor(),
secondary: Icon(CwtchIcons.block_unknown, color: settings.current().mainTextColor()),
), ),
SwitchListTile( SwitchListTile(
title: Text(AppLocalizations.of(context)!.experimentsEnabled, style: TextStyle(color: settings.current().mainTextColor())), title: Text(AppLocalizations.of(context)!.experimentsEnabled, style: TextStyle(color: settings.current().mainTextColor())),
@ -126,7 +131,9 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
// Save Settings... // Save Settings...
saveSettings(context); saveSettings(context);
}, },
secondary: Icon(Icons.science, color: settings.current().mainTextColor()), activeTrackColor: settings.theme.defaultButtonActiveColor(),
inactiveTrackColor: settings.theme.defaultButtonDisabledColor(),
secondary: Icon(CwtchIcons.enable_experiments, color: settings.current().mainTextColor()),
), ),
Visibility( Visibility(
visible: settings.experimentsEnabled, visible: settings.experimentsEnabled,
@ -135,7 +142,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
SwitchListTile( SwitchListTile(
title: Text(AppLocalizations.of(context)!.enableGroups, style: TextStyle(color: settings.current().mainTextColor())), title: Text(AppLocalizations.of(context)!.enableGroups, style: TextStyle(color: settings.current().mainTextColor())),
subtitle: Text(AppLocalizations.of(context)!.descriptionExperimentsGroups), subtitle: Text(AppLocalizations.of(context)!.descriptionExperimentsGroups),
value: settings.experiments.containsKey(TapirGroupsExperiment) && settings.experiments[TapirGroupsExperiment]!, value: settings.isExperimentEnabled(TapirGroupsExperiment),
onChanged: (bool value) { onChanged: (bool value) {
if (value) { if (value) {
settings.enableExperiment(TapirGroupsExperiment); settings.enableExperiment(TapirGroupsExperiment);
@ -145,7 +152,9 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
// Save Settings... // Save Settings...
saveSettings(context); saveSettings(context);
}, },
secondary: Icon(Icons.group_sharp, color: settings.current().mainTextColor()), activeTrackColor: settings.theme.defaultButtonActiveColor(),
inactiveTrackColor: settings.theme.defaultButtonDisabledColor(),
secondary: Icon(CwtchIcons.enable_groups, color: settings.current().mainTextColor()),
), ),
], ],
)), )),

View File

@ -1,3 +1,4 @@
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:cwtch/model.dart'; import 'package:cwtch/model.dart';
import 'package:cwtch/widgets/buttontextfield.dart'; import 'package:cwtch/widgets/buttontextfield.dart';
@ -135,12 +136,12 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
height: 20, height: 20,
), ),
Tooltip( Tooltip(
message: AppLocalizations.of(context)!.rejectGroupBtn, message: AppLocalizations.of(context)!.leaveGroup,
child: ElevatedButton.icon( child: ElevatedButton.icon(
onPressed: () { onPressed: () {
showAlertDialog(context); showAlertDialog(context);
}, },
icon: Icon(Icons.delete), icon: Icon(CwtchIcons.leave_group),
label: Text(AppLocalizations.of(context)!.leaveGroup), label: Text(AppLocalizations.of(context)!.leaveGroup),
)) ))
]) ])

View File

@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cwtch/views/peersettingsview.dart'; import 'package:cwtch/views/peersettingsview.dart';
@ -49,7 +50,10 @@ class _MessageViewState extends State<MessageView> {
//IconButton(icon: Icon(Icons.chat), onPressed: _pushContactSettings), //IconButton(icon: Icon(Icons.chat), onPressed: _pushContactSettings),
//IconButton(icon: Icon(Icons.list), onPressed: _pushContactSettings), //IconButton(icon: Icon(Icons.list), onPressed: _pushContactSettings),
//IconButton(icon: Icon(Icons.push_pin), onPressed: _pushContactSettings), //IconButton(icon: Icon(Icons.push_pin), onPressed: _pushContactSettings),
IconButton(icon: Icon(Icons.settings), tooltip: AppLocalizations.of(context)!.conversationSettings, onPressed: _pushContactSettings), IconButton(
icon: Provider.of<ContactInfoState>(context, listen: false).isGroup == true ? Icon(CwtchIcons.group_settings_24px) : Icon(CwtchIcons.peer_settings_24px),
tooltip: AppLocalizations.of(context)!.conversationSettings,
onPressed: _pushContactSettings),
], ],
), ),
body: Padding(padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 108.0), child: MessageList()), body: Padding(padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 108.0), child: MessageList()),
@ -134,11 +138,11 @@ class _MessageViewState extends State<MessageView> {
focusedBorder: InputBorder.none, focusedBorder: InputBorder.none,
enabled: true, enabled: true,
prefixIcon: IconButton( prefixIcon: IconButton(
icon: Icon(Icons.insert_invitation, size: 24, color: Provider.of<Settings>(context).theme.mainTextColor()), icon: Icon(CwtchIcons.send_invite, size: 24, color: Provider.of<Settings>(context).theme.mainTextColor()),
tooltip: "Send a contact or group invite", tooltip: "Send a contact or group invite",
onPressed: () => _modalSendInvitation(context)), onPressed: () => _modalSendInvitation(context)),
suffixIcon: IconButton( suffixIcon: IconButton(
icon: Icon(Icons.send, size: 24, color: Provider.of<Settings>(context).theme.mainTextColor()), icon: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of<Settings>(context).theme.mainTextColor()),
tooltip: "Send Message", tooltip: "Send Message",
onPressed: _sendMessage, onPressed: _sendMessage,
), ),
@ -173,7 +177,9 @@ class _MessageViewState extends State<MessageView> {
), ),
ChangeNotifierProvider.value( ChangeNotifierProvider.value(
value: Provider.of<ProfileInfoState>(ctx, listen: false), value: Provider.of<ProfileInfoState>(ctx, listen: false),
child: DropdownContacts(onChanged: (newVal) { child: DropdownContacts(filter: (contact) {
return contact.onion != Provider.of<ContactInfoState>(context).onion;
}, onChanged: (newVal) {
setState(() { setState(() {
this.selectedContact = newVal; this.selectedContact = newVal;
}); });

View File

@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:cwtch/model.dart'; import 'package:cwtch/model.dart';
import 'package:cwtch/widgets/buttontextfield.dart'; import 'package:cwtch/widgets/buttontextfield.dart';
@ -138,12 +139,14 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(profileOnion, setPeerAttributeJson); Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(profileOnion, setPeerAttributeJson);
} }
}, },
secondary: Icon(Icons.block, color: settings.current().mainTextColor()), activeTrackColor: settings.theme.defaultButtonActiveColor(),
inactiveTrackColor: settings.theme.defaultButtonDisabledColor(),
secondary: Icon(CwtchIcons.block_peer, color: settings.current().mainTextColor()),
), ),
ListTile( ListTile(
title: Text(AppLocalizations.of(context)!.savePeerHistory, style: TextStyle(color: settings.current().mainTextColor())), title: Text(AppLocalizations.of(context)!.savePeerHistory, style: TextStyle(color: settings.current().mainTextColor())),
subtitle: Text(AppLocalizations.of(context)!.savePeerHistoryDescription), subtitle: Text(AppLocalizations.of(context)!.savePeerHistoryDescription),
leading: Icon(Icons.history_sharp, color: settings.current().mainTextColor()), leading: Icon(CwtchIcons.peer_history, color: settings.current().mainTextColor()),
trailing: DropdownButton( trailing: DropdownButton(
value: Provider.of<ContactInfoState>(context).savePeerHistory == "DefaultDeleteHistory" value: Provider.of<ContactInfoState>(context).savePeerHistory == "DefaultDeleteHistory"
? AppLocalizations.of(context)!.dontSavePeerHistory ? AppLocalizations.of(context)!.dontSavePeerHistory

View File

@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cwtch/settings.dart'; import 'package:cwtch/settings.dart';
import 'package:cwtch/views/torstatusview.dart'; import 'package:cwtch/views/torstatusview.dart';
@ -11,6 +12,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 '../torstatus.dart';
import 'addeditprofileview.dart'; import 'addeditprofileview.dart';
import 'globalsettingsview.dart'; import 'globalsettingsview.dart';
@ -57,10 +59,16 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
Expanded(child: Text(AppLocalizations.of(context)!.titleManageProfiles, style: TextStyle(color: settings.current().mainTextColor()))) Expanded(child: Text(AppLocalizations.of(context)!.titleManageProfiles, style: TextStyle(color: settings.current().mainTextColor())))
]), ]),
actions: [ actions: [
IconButton(icon: TorIcon(), onPressed: _pushTorStatus), IconButton(
icon: TorIcon(),
onPressed: _pushTorStatus,
tooltip: Provider.of<TorStatus>(context).progress == 100
? AppLocalizations.of(context)!.networkStatusOnline
: (Provider.of<TorStatus>(context).progress == 0 ? AppLocalizations.of(context)!.networkStatusDisconnected : AppLocalizations.of(context)!.networkStatusAttemptingTor),
),
IconButton(icon: Icon(Icons.bug_report_outlined), onPressed: _setLoggingLevelDebug), IconButton(icon: Icon(Icons.bug_report_outlined), onPressed: _setLoggingLevelDebug),
IconButton( IconButton(
icon: Icon(Icons.lock_open), icon: Icon(CwtchIcons.lock_open_24px),
tooltip: AppLocalizations.of(context)!.tooltipUnlockProfiles, tooltip: AppLocalizations.of(context)!.tooltipUnlockProfiles,
onPressed: _modalUnlockProfiles, onPressed: _modalUnlockProfiles,
), ),

View File

@ -3,6 +3,10 @@ import 'package:provider/provider.dart';
import '../model.dart'; import '../model.dart';
bool noFilter(ContactInfoState peer) {
return true;
}
// Dropdown menu populated from Provider.of<ProfileInfoState>'s contact list // Dropdown menu populated from Provider.of<ProfileInfoState>'s contact list
// Includes both peers and groups; begins empty/nothing selected // Includes both peers and groups; begins empty/nothing selected
// Displays nicknames to UI but uses handles as values // Displays nicknames to UI but uses handles as values
@ -10,8 +14,10 @@ import '../model.dart';
class DropdownContacts extends StatefulWidget { class DropdownContacts extends StatefulWidget {
DropdownContacts({ DropdownContacts({
required this.onChanged, required this.onChanged,
this.filter = noFilter,
}); });
final Function(dynamic) onChanged; final Function(dynamic) onChanged;
final bool Function(ContactInfoState) filter;
@override @override
_DropdownContactsState createState() => _DropdownContactsState(); _DropdownContactsState createState() => _DropdownContactsState();
@ -24,7 +30,7 @@ class _DropdownContactsState extends State<DropdownContacts> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return DropdownButton( return DropdownButton(
value: this.selected, value: this.selected,
items: Provider.of<ProfileInfoState>(context, listen: false).contactList.contacts.map<DropdownMenuItem<String>>((ContactInfoState contact) { items: Provider.of<ProfileInfoState>(context, listen: false).contactList.contacts.where(widget.filter).map<DropdownMenuItem<String>>((ContactInfoState contact) {
return DropdownMenuItem<String>(value: contact.onion, child: Text(contact.nickname)); return DropdownMenuItem<String>(value: contact.onion, child: Text(contact.nickname));
}).toList(), }).toList(),
onChanged: (String? newVal) { onChanged: (String? newVal) {

View File

@ -50,7 +50,7 @@ class _ContactRowState extends State<ContactRow> {
))), ))),
Padding( Padding(
padding: const EdgeInsets.all(5.0), padding: const EdgeInsets.all(5.0),
child: contact.isInvitation != null && contact.isInvitation child: contact.isInvitation == true
? Wrap(direction: Axis.vertical, children: <Widget>[ ? Wrap(direction: Axis.vertical, children: <Widget>[
IconButton( IconButton(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,

View File

@ -1,5 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:cwtch/widgets/malformedbubble.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../main.dart'; import '../main.dart';
@ -53,24 +55,33 @@ class InvitationBubbleState extends State<InvitationBubble> {
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor()))); style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor())));
// todo: translations // todo: translations
// If we receive an invite for ourselves, treat it as a bug. The UI no longer allows this so it could have only come from
// some kind of malfeasance.
var selfInvite = Provider.of<MessageState>(context).inviteNick == Provider.of<ProfileInfoState>(context).onion;
if (selfInvite) {
return MalformedBubble();
}
var wdgMessage = fromMe var wdgMessage = fromMe
? senderInviteChrome("You sent an invitation for", isGroup ? "a group" : Provider.of<MessageState>(context).message, myKey) ? senderInviteChrome(AppLocalizations.of(context)!.sendAnInvitation,
: inviteChrome(isGroup ? AppLocalizations.of(context)!.inviteToGroup : "This is a contact suggestion for:", Provider.of<MessageState>(context).inviteNick, isGroup ? Provider.of<ProfileInfoState>(context).contactList.getContact(Provider.of<MessageState>(context).inviteTarget)!.nickname : Provider.of<MessageState>(context).message, myKey)
Provider.of<MessageState>(context).inviteTarget, myKey); : (inviteChrome(isGroup ? AppLocalizations.of(context)!.inviteToGroup : AppLocalizations.of(context)!.contactSuggestion, Provider.of<MessageState>(context).inviteNick,
Provider.of<MessageState>(context).inviteTarget, myKey));
Widget wdgDecorations; Widget wdgDecorations;
if (fromMe) { if (fromMe) {
wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageState>(context).ackd, errored: Provider.of<MessageState>(context).error, fromMe: fromMe, prettyDate: prettyDate); wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageState>(context).ackd, errored: Provider.of<MessageState>(context).error, fromMe: fromMe, prettyDate: prettyDate);
} else if (isAccepted) { } else if (isAccepted) {
wdgDecorations = Text("Accepted!"); wdgDecorations = Text(AppLocalizations.of(context)!.accepted + '\u202F');
} else if (this.rejected) { } else if (this.rejected) {
wdgDecorations = Text("Rejected."); wdgDecorations = Text(AppLocalizations.of(context)!.rejected + '\u202F');
} else { } else {
wdgDecorations = Center( wdgDecorations = Center(
widthFactor: 1, widthFactor: 1,
child: Row(children: [ child: Wrap(children: [
Padding(padding: EdgeInsets.all(5), child: TextButton(child: Text("Reject"), onPressed: _btnReject)), Padding(padding: EdgeInsets.all(5), child: TextButton(child: Text(AppLocalizations.of(context)!.rejectGroupBtn + '\u202F'), onPressed: _btnReject)),
Padding(padding: EdgeInsets.all(5), child: TextButton(child: Text("Accept"), onPressed: _btnAccept)), Padding(padding: EdgeInsets.all(5), child: TextButton(child: Text(AppLocalizations.of(context)!.acceptGroupBtn + '\u202F'), onPressed: _btnAccept)),
])); ]));
} }
@ -94,15 +105,16 @@ class InvitationBubbleState extends State<InvitationBubble> {
widthFactor: 1.0, widthFactor: 1.0,
child: Padding( child: Padding(
padding: EdgeInsets.all(9.0), padding: EdgeInsets.all(9.0),
child: Row(mainAxisSize: MainAxisSize.min, children: [ child: Wrap(runAlignment: WrapAlignment.spaceEvenly, alignment: WrapAlignment.spaceEvenly, runSpacing: 1.0, crossAxisAlignment: WrapCrossAlignment.center, children: [
Center(widthFactor: 1, child: Padding(padding: EdgeInsets.all(4), child: Icon(Icons.group_add, size: 32))), Center(widthFactor: 1, child: Padding(padding: EdgeInsets.all(10.0), child: Icon(CwtchIcons.send_invite, size: 32))),
Center( Center(
widthFactor: 1.0, widthFactor: 1.0,
child: Column( child: Column(
crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations])), children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations]),
)
]))))); ])))));
}); });
} }
@ -128,55 +140,49 @@ class InvitationBubbleState extends State<InvitationBubble> {
// Construct an invite chrome for the sender // Construct an invite chrome for the sender
Widget senderInviteChrome(String chrome, String targetName, String myKey) { Widget senderInviteChrome(String chrome, String targetName, String myKey) {
return Center( return Wrap(children: [
widthFactor: 1, SelectableText(
child: Row(children: [ chrome + '\u202F',
SelectableText( style: TextStyle(
chrome, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(),
key: Key(myKey), ),
focusNode: _focus, textAlign: TextAlign.left,
style: TextStyle( maxLines: 2,
color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), textWidthBasis: TextWidthBasis.longestLine,
), ),
textAlign: TextAlign.left, SelectableText(
textWidthBasis: TextWidthBasis.longestLine, targetName + '\u202F',
), key: Key(myKey),
SelectableText( style: TextStyle(
targetName + '\u202F', color: Provider.of<Settings>(context).theme.messageFromMeTextColor(),
key: Key(myKey), ),
focusNode: _focus, textAlign: TextAlign.left,
style: TextStyle( maxLines: 2,
color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), textWidthBasis: TextWidthBasis.longestLine,
), )
textAlign: TextAlign.left, ]);
textWidthBasis: TextWidthBasis.longestLine,
)
]));
} }
// Construct an invite chrome // Construct an invite chrome
Widget inviteChrome(String chrome, String targetName, String targetId, String myKey) { Widget inviteChrome(String chrome, String targetName, String targetId, String myKey) {
return Center( return Wrap(children: [
widthFactor: 1, SelectableText(
child: Row(children: [ chrome + '\u202F',
SelectableText( style: TextStyle(
chrome, color: Provider.of<Settings>(context).theme.messageFromOtherTextColor(),
key: Key(myKey), ),
focusNode: _focus, textAlign: TextAlign.left,
style: TextStyle( textWidthBasis: TextWidthBasis.longestLine,
color: Provider.of<Settings>(context).theme.messageFromOtherTextColor(), maxLines: 2,
), ),
textAlign: TextAlign.left, SelectableText(
textWidthBasis: TextWidthBasis.longestLine, targetName + '\u202F',
), key: Key(myKey),
SelectableText( style: TextStyle(color: Provider.of<Settings>(context).theme.messageFromOtherTextColor()),
targetName, textAlign: TextAlign.left,
key: Key(myKey), maxLines: 2,
focusNode: _focus, textWidthBasis: TextWidthBasis.longestLine,
style: TextStyle(color: Provider.of<Settings>(context).theme.messageFromOtherTextColor()), )
textAlign: TextAlign.left, ]);
textWidthBasis: TextWidthBasis.longestLine,
)
]));
} }
} }

View File

@ -1,10 +1,5 @@
import 'dart:convert'; import 'package:cwtch/cwtch_icons_icons.dart';
import 'dart:ffi';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../settings.dart';
final Color malformedColor = Color(0xFFE85DA1); final Color malformedColor = Color(0xFFE85DA1);
@ -40,15 +35,10 @@ class MalformedBubbleState extends State<MalformedBubble> {
widthFactor: 1, widthFactor: 1,
child: Padding( child: Padding(
padding: EdgeInsets.all(4), padding: EdgeInsets.all(4),
child: Image( child: Icon(
image: AssetImage("assets/core/broken_heart_24.png"), CwtchIcons.favorite_black_24dp_broken,
filterQuality: FilterQuality.medium, size: 24,
// We need some theme specific blending here...we might want to consider making this a theme level attribute ))),
colorBlendMode: BlendMode.srcIn,
color: Provider.of<Settings>(context).theme.mainTextColor(),
isAntiAlias: false,
width: 32,
height: 32))),
Center( Center(
widthFactor: 1.0, widthFactor: 1.0,
child: Column( child: Column(

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../model.dart'; import '../model.dart';
import '../settings.dart'; import '../settings.dart';
import 'messagerow.dart'; import 'messagerow.dart';
@ -15,24 +16,44 @@ class _MessageListState extends State<MessageList> {
@override @override
Widget build(BuildContext outerContext) { Widget build(BuildContext outerContext) {
bool showEphemeralWarning = (Provider.of<ContactInfoState>(context).isGroup == false && Provider.of<ContactInfoState>(context).savePeerHistory != "SaveHistory");
bool showOfflineWarning = Provider.of<ContactInfoState>(context).isOnline() == false;
bool showMessageWarning = showEphemeralWarning || showOfflineWarning;
return RepaintBoundary( return RepaintBoundary(
child: Container( child: Container(
child: Scrollbar( child: Column(children: [
isAlwaysShown: true, Visibility(
controller: ctrlr1, visible: showMessageWarning,
child: Container( child: Container(
padding: EdgeInsets.all(5.0),
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
child: showOfflineWarning
? Text(Provider.of<ContactInfoState>(context).isGroup ? AppLocalizations.of(context)!.serverConnectivityDisconnected : AppLocalizations.of(context)!.peerOfflineMessage,
textAlign: TextAlign.center)
// Only show the ephemeral status for peer conversations, not for groups...
: (showEphemeralWarning
? Text(AppLocalizations.of(context)!.chatHistoryDefault, textAlign: TextAlign.center)
:
// We are not allowed to put null here, so put an empty text widge
Text("")),
)),
Expanded(
child: Scrollbar(
controller: ctrlr1,
child: Container(
// Only show broken heart is the contact is offline... // Only show broken heart is the contact is offline...
decoration: BoxDecoration( decoration: BoxDecoration(
image: Provider.of<ContactInfoState>(outerContext).isOnline() image: Provider.of<ContactInfoState>(outerContext).isOnline()
? null ? null
: DecorationImage( : DecorationImage(
fit: BoxFit.contain, fit: BoxFit.scaleDown,
alignment: Alignment.center,
image: AssetImage("assets/core/negative_heart_512px.png"), image: AssetImage("assets/core/negative_heart_512px.png"),
colorFilter: ColorFilter.mode(Provider.of<Settings>(context).theme.mainTextColor(), BlendMode.srcIn))), colorFilter: ColorFilter.mode(Provider.of<Settings>(context).theme.hilightElementTextColor(), BlendMode.srcIn))),
child: ListView.builder( child: ListView.builder(
controller: ctrlr1, controller: ctrlr1,
itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages, itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages,
reverse: true, reverse: true, // NOTE: There seems to be a bug in flutter that corrects the mouse wheel scroll, but not the drag direction...
itemBuilder: (itemBuilderContext, index) { itemBuilder: (itemBuilderContext, index) {
var trueIndex = Provider.of<ContactInfoState>(outerContext).totalMessages - index - 1; var trueIndex = Provider.of<ContactInfoState>(outerContext).totalMessages - index - 1;
return ChangeNotifierProvider( return ChangeNotifierProvider(
@ -51,7 +72,7 @@ class _MessageListState extends State<MessageList> {
return RepaintBoundary(child: MessageRow(key: Provider.of<ContactInfoState>(bcontext).getMessageKey(idx))); return RepaintBoundary(child: MessageRow(key: Provider.of<ContactInfoState>(bcontext).getMessageKey(idx)));
}); });
}, },
), ))))
)))); ])));
} }
} }

View File

@ -31,10 +31,8 @@ class _MessageRowState extends State<MessageRow> {
fromMe = false; fromMe = false;
} }
Widget wdgBubble = Flexible( Widget wdgBubble =
flex: 3, Flexible(flex: 3, fit: FlexFit.loose, child: Provider.of<MessageState>(context).loaded == true ? widgetForOverlay(Provider.of<MessageState>(context).overlay) : MessageLoadingBubble());
fit: FlexFit.loose,
child: malformed ? MalformedBubble() : (Provider.of<MessageState>(context).loaded == true ? widgetForOverlay(Provider.of<MessageState>(context).overlay) : MessageLoadingBubble()));
Widget wdgIcons = Icon(Icons.delete_forever_outlined, color: Provider.of<Settings>(context).theme.dropShadowColor()); Widget wdgIcons = Icon(Icons.delete_forever_outlined, color: Provider.of<Settings>(context).theme.dropShadowColor());
Widget wdgSpacer = Expanded(child: SizedBox(width: 60, height: 10)); Widget wdgSpacer = Expanded(child: SizedBox(width: 60, height: 10));
var widgetRow = <Widget>[]; var widgetRow = <Widget>[];
@ -96,7 +94,10 @@ class _MessageRowState extends State<MessageRow> {
final setPeerAttributeJson = jsonEncode(setPeerAttribute); final setPeerAttributeJson = jsonEncode(setPeerAttribute);
Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(profileOnion, setPeerAttributeJson); Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(profileOnion, setPeerAttributeJson);
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.successfullAddedContact)); final snackBar = SnackBar(
content: Text(AppLocalizations.of(context)!.successfullAddedContact),
duration: Duration(seconds: 2),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar); ScaffoldMessenger.of(context).showSnackBar(snackBar);
} }
} }

View File

@ -1,3 +1,4 @@
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../settings.dart'; import '../settings.dart';
@ -5,7 +6,7 @@ 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.
class CwtchPasswordField extends StatefulWidget { class CwtchPasswordField extends StatefulWidget {
CwtchPasswordField({required this.controller, required this.validator, this.action, this.autofocus = true}); CwtchPasswordField({required this.controller, required this.validator, this.action, this.autofocus = false});
final TextEditingController controller; final TextEditingController controller;
final FormFieldValidator validator; final FormFieldValidator validator;
final Function(String)? action; final Function(String)? action;
@ -43,7 +44,7 @@ class _CwtchTextFieldState extends State<CwtchPasswordField> {
obscureText = !obscureText; obscureText = !obscureText;
}); });
}, },
icon: Icon((obscureText ? Icons.remove_red_eye : Icons.remove_red_eye_outlined), semanticLabel: label), icon: Icon((obscureText ? CwtchIcons.eye_closed : CwtchIcons.eye_open), semanticLabel: label),
tooltip: label, tooltip: label,
color: theme.current().mainTextColor(), color: theme.current().mainTextColor(),
highlightColor: theme.current().defaultButtonColor(), highlightColor: theme.current().defaultButtonColor(),

View File

@ -7,11 +7,12 @@ doNothing(String x) {}
// 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 CwtchTextField extends StatefulWidget { class CwtchTextField extends StatefulWidget {
CwtchTextField({required this.controller, required this.labelText, this.validator = null, this.onChanged = doNothing}); CwtchTextField({required this.controller, required this.labelText, this.validator = null, this.autofocus = false, this.onChanged = doNothing});
final TextEditingController controller; final TextEditingController controller;
final String labelText; final String labelText;
final FormFieldValidator? validator; final FormFieldValidator? validator;
final Function(String) onChanged; final Function(String) onChanged;
final bool autofocus;
@override @override
_CwtchTextFieldState createState() => _CwtchTextFieldState(); _CwtchTextFieldState createState() => _CwtchTextFieldState();
@ -25,6 +26,7 @@ class _CwtchTextFieldState extends State<CwtchTextField> {
controller: widget.controller, controller: widget.controller,
validator: widget.validator, validator: widget.validator,
onChanged: widget.onChanged, onChanged: widget.onChanged,
autofocus: widget.autofocus,
decoration: InputDecoration( decoration: InputDecoration(
labelText: widget.labelText, labelText: widget.labelText,
labelStyle: TextStyle(color: theme.current().mainTextColor(), backgroundColor: theme.current().textfieldBackgroundColor()), labelStyle: TextStyle(color: theme.current().mainTextColor(), backgroundColor: theme.current().textfieldBackgroundColor()),

View File

@ -1,3 +1,4 @@
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.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';
@ -16,13 +17,9 @@ class _TorIconState extends State<TorIcon> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return RepaintBoundary( return RepaintBoundary(
child: Image( child: Icon(
image: AssetImage(Provider.of<TorStatus>(context).progress == 0 Provider.of<TorStatus>(context).progress == 0 ? CwtchIcons.onion_off : (Provider.of<TorStatus>(context).progress == 100 ? CwtchIcons.onion_on : CwtchIcons.onion_waiting),
? "assets/core/Tor_OFF.png"
: (Provider.of<TorStatus>(context).progress == 100 ? "assets/core/Tor_icon.png" : "assets/core/Tor_Booting_up.png")),
// Color the onion per the text color...
color: Provider.of<Settings>(context).theme.mainTextColor(), color: Provider.of<Settings>(context).theme.mainTextColor(),
colorBlendMode: BlendMode.srcIn,
semanticLabel: Provider.of<TorStatus>(context).progress == 100 semanticLabel: Provider.of<TorStatus>(context).progress == 100
? AppLocalizations.of(context)!.networkStatusOnline ? AppLocalizations.of(context)!.networkStatusOnline
: (Provider.of<TorStatus>(context).progress == 0 ? AppLocalizations.of(context)!.networkStatusDisconnected : AppLocalizations.of(context)!.networkStatusAttemptingTor), : (Provider.of<TorStatus>(context).progress == 0 ? AppLocalizations.of(context)!.networkStatusDisconnected : AppLocalizations.of(context)!.networkStatusAttemptingTor),

View File

@ -2,6 +2,8 @@
// Generated file. Do not edit. // Generated file. Do not edit.
// //
// clang-format off
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <window_size/window_size_plugin.h> #include <window_size/window_size_plugin.h>

View File

@ -2,6 +2,8 @@
// Generated file. Do not edit. // Generated file. Do not edit.
// //
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_ #ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_ #define GENERATED_PLUGIN_REGISTRANT_

View File

@ -21,7 +21,7 @@ packages:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.5.0" version: "2.7.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -191,7 +191,7 @@ packages:
name: meta name: meta
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.0" version: "1.4.0"
nested: nested:
dependency: transitive dependency: transitive
description: description:
@ -392,7 +392,7 @@ packages:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.0" version: "0.4.0"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -450,7 +450,7 @@ packages:
name: xml name: xml
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.1.1" version: "5.1.2"
sdks: sdks:
dart: ">=2.13.0 <3.0.0" dart: ">=2.13.0 <3.0.0"
flutter: ">=1.20.0" flutter: ">=1.20.0"

View File

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+9 version: 1.0.0+10
environment: environment:
sdk: ">=2.12.0 <3.0.0" sdk: ">=2.12.0 <3.0.0"
@ -95,6 +95,11 @@ flutter:
- assets/profiles/ - assets/profiles/
- assets/servers/ - assets/servers/
fonts:
- family: CwtchIcons
fonts:
- asset: assets/fonts/CwtchIcons.ttf
# To add custom fonts to your application, add a fonts section here, # To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a # in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a # "family" key with the font family name, and a "fonts" key with a

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.15) cmake_minimum_required(VERSION 3.15)
project(flutter_app LANGUAGES CXX) project(cwtch LANGUAGES CXX)
set(BINARY_NAME "flutter_app") set(BINARY_NAME "cwtch")
cmake_policy(SET CMP0063 NEW) cmake_policy(SET CMP0063 NEW)

View File

@ -6,6 +6,9 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <window_size/window_size_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
WindowSizePluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WindowSizePlugin"));
} }

View File

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
window_size
) )
set(PLUGIN_BUNDLED_LIBRARIES) set(PLUGIN_BUNDLED_LIBRARIES)

View File

@ -52,7 +52,16 @@ END
// Icon with lowest ID value placed first to ensure application icon // Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems. // remains consistent on all systems.
IDI_APP_ICON ICON "resources\\app_icon.ico" IDI_APP_ICON_LG ICON "resources\\knot_256.ico"
IDI_APP_ICON_SM ICON "resources\\knot_64.ico"
IDI_APP_ICON_256 ICON "resources\\knot_256.ico"
IDI_APP_ICON_128 ICON "resources\\knot_128.ico"
IDI_APP_ICON_64 ICON "resources\\knot_64.ico"
IDI_APP_ICON_48 ICON "resources\\knot_48.ico"
IDI_APP_ICON_32 ICON "resources\\knot_32.ico"
IDI_APP_ICON_16 ICON "resources\\knot_16.ico"
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
@ -89,13 +98,13 @@ BEGIN
BEGIN BEGIN
BLOCK "040904e4" BLOCK "040904e4"
BEGIN BEGIN
VALUE "CompanyName", "com.example" "\0" VALUE "CompanyName", "Open Privacy Research Society" "\0"
VALUE "FileDescription", "A new Flutter project." "\0" VALUE "FileDescription", "Cwtch Instant Messenger" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0" VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "flutter_app" "\0" VALUE "InternalName", "cwtch" "\0"
VALUE "LegalCopyright", "Copyright (C) 2021 com.example. All rights reserved." "\0" VALUE "LegalCopyright", "Copyright (C) 2021 Open Privacy Research Society. All rights reserved." "\0"
VALUE "OriginalFilename", "flutter_app.exe" "\0" VALUE "OriginalFilename", "cwtch.exe" "\0"
VALUE "ProductName", "flutter_app" "\0" VALUE "ProductName", "Cwtch" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0"
END END
END END

View File

@ -30,7 +30,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
FlutterWindow window(&run_loop, project); FlutterWindow window(&run_loop, project);
Win32Window::Point origin(10, 10); Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720); Win32Window::Size size(1280, 720);
if (!window.CreateAndShow(L"flutter_app", origin, size)) { if (!window.CreateAndShow(L"cwtch", origin, size)) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
window.SetQuitOnClose(true); window.SetQuitOnClose(true);

View File

@ -2,7 +2,16 @@
// Microsoft Visual C++ generated include file. // Microsoft Visual C++ generated include file.
// Used by Runner.rc // Used by Runner.rc
// //
#define IDI_APP_ICON 101 #define IDI_APP_ICON_LG 101
#define IDI_APP_ICON_SM 102
#define IDI_APP_ICON_256 103
#define IDI_APP_ICON_128 104
#define IDI_APP_ICON_64 105
#define IDI_APP_ICON_48 106
#define IDI_APP_ICON_32 107
#define IDI_APP_ICON_16 108
// Next default values for new objects // Next default values for new objects
// //

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,4 +1,5 @@
#include "win32_window.h" #include "win32_window.h"
#include "winuser.h"
#include <flutter_windows.h> #include <flutter_windows.h>
@ -70,19 +71,49 @@ WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
const wchar_t* WindowClassRegistrar::GetWindowClass() { const wchar_t* WindowClassRegistrar::GetWindowClass() {
if (!class_registered_) { if (!class_registered_) {
WNDCLASS window_class{}; WNDCLASSEX window_class{};
window_class.cbSize = sizeof(WNDCLASSEX);
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
window_class.lpszClassName = kWindowClassName; window_class.lpszClassName = kWindowClassName;
window_class.style = CS_HREDRAW | CS_VREDRAW; window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.cbClsExtra = 0; window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0; window_class.cbWndExtra = 0;
window_class.hInstance = GetModuleHandle(nullptr); window_class.hInstance = GetModuleHandle(nullptr);
int icon_sz = GetSystemMetrics(SM_CXICON);
int iconh_id = IDI_APP_ICON_32;
if (icon_sz > 128) {
iconh_id = IDI_APP_ICON_256;
} else if (icon_sz > 64) {
iconh_id = IDI_APP_ICON_128;
} else if (icon_sz > 48) {
iconh_id = IDI_APP_ICON_64;
} else if (icon_sz > 32) {
iconh_id = IDI_APP_ICON_48;
}
window_class.hIcon = window_class.hIcon =
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); LoadIcon(window_class.hInstance, MAKEINTRESOURCE(iconh_id));
int icon_sm_sz = GetSystemMetrics(SM_CXSMICON);
int iconsmh_id = IDI_APP_ICON_16;
if (icon_sm_sz > 128) {
iconsmh_id = IDI_APP_ICON_256;
} else if (icon_sm_sz > 64) {
iconsmh_id = IDI_APP_ICON_128;
} else if (icon_sm_sz > 48) {
iconsmh_id = IDI_APP_ICON_64;
} else if (icon_sm_sz > 32) {
iconsmh_id = IDI_APP_ICON_48;
} else if (icon_sm_sz > 16) {
iconsmh_id = IDI_APP_ICON_32;
}
window_class.hIconSm =
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(iconsmh_id));
window_class.hbrBackground = 0; window_class.hbrBackground = 0;
window_class.lpszMenuName = nullptr; window_class.lpszMenuName = nullptr;
window_class.lpfnWndProc = Win32Window::WndProc; window_class.lpfnWndProc = Win32Window::WndProc;
RegisterClass(&window_class); RegisterClassEx(&window_class);
class_registered_ = true; class_registered_ = true;
} }
return kWindowClassName; return kWindowClassName;