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.png deploy/linux
- 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
- cd deploy
- mv linux cwtch
@ -230,6 +231,12 @@ steps:
- mkdir deploy
- move build\\windows\\runner\\Release $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 "Compress-Archive -Path $Env:builddir -DestinationPath $Env:zip"
- powershell -command "(Get-FileHash *.zip -Algorithm sha512).Hash" > $Env:sha

View File

@ -1,6 +1,6 @@
# flwtch
A new Flutter application.
A Flutter based Cwtch UI
## Getting Started
@ -8,13 +8,19 @@ click the play button in android studio
### 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
### Windows
- libCwtch-go: the result of `make windows`, `libCwtch.dll` should be placed in the source root
- tor is bundled in `windors/Tor`
- run `fetch-libcwtch-go.ps1` to get `libCwtch.dll` which is required to run
- 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

View File

@ -17,10 +17,10 @@
style="enable-background:new 0 0 24 24;"
xml:space="preserve"
sodipodi:docname="negative_heart_24px.svg"
inkscape:export-filename="/home/sarah/AndroidStudioProjects/flutter_app/assets/core/negative_heart_512px.png"
inkscape:export-xdpi="4096"
inkscape:export-ydpi="4096"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"><metadata
inkscape:export-filename="/home/sarah/PARA/projects/cwtch/assets/core/negative_heart_256px.png"
inkscape:export-xdpi="1024"
inkscape:export-ydpi="1024"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><metadata
id="metadata14"><rdf:RDF><cc:Work
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
@ -34,14 +34,14 @@
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1015"
inkscape:window-height="1020"
id="namedview10"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="12"
inkscape:cy="14.687942"
inkscape:cx="-5.8983051"
inkscape:cy="14.281162"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-y="31"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<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;
case "AcceptGroupInvite":
print("accept group invite: $data");
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.isInvitation = false;
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now());
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",
"accepted": "",
"acceptGroupBtn": "Annehmen",
"acceptGroupInviteLabel": "Möchtest Du die Einladung annehmen",
"acknowledgedLabel": "bestätigt",
@ -17,7 +18,9 @@
"builddate": "Aufgebaut auf: %2",
"bulletinsBtn": "Meldungen",
"chatBtn": "Chat",
"chatHistoryDefault": "",
"contactAlreadyExists": "",
"contactSuggestion": "",
"conversationSettings": "",
"copiedClipboardNotification": "in die Zwischenablage kopiert",
"copiedToClipboardNotification": "in die Zwischenablage kopiert",
@ -120,6 +123,7 @@
"radioNoPassword": "Unverschlüsselt (kein Passwort)",
"radioUsePassword": "Passwort",
"reallyLeaveThisGroupPrompt": "",
"rejected": "",
"rejectGroupBtn": "Ablehnen",
"saveBtn": "Speichern",
"savePeerHistory": "Peer-Verlauf speichern",
@ -127,6 +131,7 @@
"saveProfileBtn": "Profil speichern",
"search": "Suche...",
"searchList": "",
"sendAnInvitation": "",
"server": "Server",
"serverConnectivityConnected": "Server verbunden",
"serverConnectivityDisconnected": "Server getrennt",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -64,13 +64,13 @@ class ContactListState extends ChangeNotifier {
bool get isFiltered => _filter != "";
String get filter => _filter;
set filter(String newVal) {
_filter = newVal;
_filter = newVal.toLowerCase();
notifyListeners();
}
List<ContactInfoState> filteredList() {
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) {
@ -382,7 +382,7 @@ class MessageState extends ChangeNotifier {
late String _inviteNick;
late DateTime _timestamp;
late String _senderOnion;
late String _senderImage;
String? _senderImage;
late String _signature = "";
late bool _ackd = false;
late bool _error = false;
@ -404,10 +404,10 @@ class MessageState extends ChangeNotifier {
get timestamp => this._timestamp;
bool get ackd => this._ackd;
bool get error => this._error;
get malformed => this._malformed;
bool get malformed => this._malformed;
bool get loaded => this._loaded;
get senderOnion => this._senderOnion;
get senderImage => this._senderImage;
get loaded => this._loaded;
get signature => this._signature;
get isInvite => this.overlay == 100 || this.overlay == 101;
get inviteTarget => this._inviteTarget;
@ -423,6 +423,16 @@ class MessageState extends ChangeNotifier {
notifyListeners();
}
set malformed(bool newVal) {
this._malformed = newVal;
notifyListeners();
}
set loaded(bool newVal) {
this._loaded = newVal;
notifyListeners();
}
void tryLoad(BuildContext context) {
Provider.of<FlwtchState>(context, listen: false).cwtch.GetMessage(profileOnion, contactHandle, messageIndex).then((jsonMessage) {
try {
@ -430,7 +440,6 @@ class MessageState extends ChangeNotifier {
dynamic messageWrapper = jsonDecode(jsonMessage);
if (messageWrapper['Message'] == null || messageWrapper['Message'] == '' || messageWrapper['Message'] == '{}') {
this._senderOnion = profileOnion;
//todo: remove once sent group messages are prestored
Future.delayed(const Duration(milliseconds: 2), () {
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
this.ackd = messageWrapper['Acknowledged'];
@ -472,7 +481,9 @@ class MessageState extends ChangeNotifier {
this.error = true;
}
} 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(),
appBarTheme: AppBarTheme(
backgroundColor: opaque.current().backgroundPaneColor(),
titleTextStyle: TextStyle(
color: opaque.current().mainTextColor(),
),
actionsIconTheme: IconThemeData(
color: opaque.current().mainTextColor(),
),
),
backgroundColor: opaque.current().backgroundPaneColor(),
titleTextStyle: TextStyle(
color: opaque.current().mainTextColor(),
),
actionsIconTheme: IconThemeData(
color: opaque.current().mainTextColor(),
)),
bottomNavigationBarTheme: BottomNavigationBarThemeData(type: BottomNavigationBarType.fixed, backgroundColor: opaque.current().backgroundHilightElementColor()),
textButtonTheme: TextButtonThemeData(
style: ButtonStyle(
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()))),
dialogTheme: DialogTheme(
backgroundColor: opaque.current().backgroundPaneColor(),

View File

@ -16,7 +16,8 @@ class Settings extends ChangeNotifier {
Locale locale;
late PackageInfo packageInfo;
OpaqueThemeType theme;
late bool experimentsEnabled;
// explicitly set experiments to false until told otherwise...
bool experimentsEnabled = false;
HashMap<String, bool> experiments = HashMap.identity();
late bool blockUnknownConnections;
@ -38,6 +39,18 @@ class Settings extends ChangeNotifier {
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
/// be sent to the function and new settings will be instantiated based on the contents.
handleUpdate(dynamic settings) {

View File

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

View File

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

View File

@ -1,3 +1,4 @@
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/material.dart';
import 'package:cwtch/views/torstatusview.dart';
import 'package:cwtch/widgets/contactrow.dart';
@ -66,7 +67,7 @@ class _ContactsViewState extends State<ContactsView> {
floatingActionButton: FloatingActionButton(
onPressed: _pushAddContact,
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());
}

View File

@ -1,5 +1,6 @@
import 'dart:convert';
import 'dart:io';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:flutter/material.dart';
import 'package:cwtch/opaque.dart';
@ -46,7 +47,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
child: Column(children: [
ListTile(
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(
value: Provider.of<Settings>(context).locale.languageCode,
onChanged: (String? newValue) {
@ -74,7 +75,9 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
// Save Settings...
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(
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...
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(
title: Text(AppLocalizations.of(context)!.experimentsEnabled, style: TextStyle(color: settings.current().mainTextColor())),
@ -126,7 +131,9 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
// Save Settings...
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(
visible: settings.experimentsEnabled,
@ -135,7 +142,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
SwitchListTile(
title: Text(AppLocalizations.of(context)!.enableGroups, style: TextStyle(color: settings.current().mainTextColor())),
subtitle: Text(AppLocalizations.of(context)!.descriptionExperimentsGroups),
value: settings.experiments.containsKey(TapirGroupsExperiment) && settings.experiments[TapirGroupsExperiment]!,
value: settings.isExperimentEnabled(TapirGroupsExperiment),
onChanged: (bool value) {
if (value) {
settings.enableExperiment(TapirGroupsExperiment);
@ -145,7 +152,9 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
// Save Settings...
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:cwtch/model.dart';
import 'package:cwtch/widgets/buttontextfield.dart';
@ -135,12 +136,12 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
height: 20,
),
Tooltip(
message: AppLocalizations.of(context)!.rejectGroupBtn,
message: AppLocalizations.of(context)!.leaveGroup,
child: ElevatedButton.icon(
onPressed: () {
showAlertDialog(context);
},
icon: Icon(Icons.delete),
icon: Icon(CwtchIcons.leave_group),
label: Text(AppLocalizations.of(context)!.leaveGroup),
))
])

View File

@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.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.list), 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()),
@ -134,11 +138,11 @@ class _MessageViewState extends State<MessageView> {
focusedBorder: InputBorder.none,
enabled: true,
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",
onPressed: () => _modalSendInvitation(context)),
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",
onPressed: _sendMessage,
),
@ -173,7 +177,9 @@ class _MessageViewState extends State<MessageView> {
),
ChangeNotifierProvider.value(
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(() {
this.selectedContact = newVal;
});

View File

@ -1,4 +1,5 @@
import 'dart:convert';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/services.dart';
import 'package:cwtch/model.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);
}
},
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(
title: Text(AppLocalizations.of(context)!.savePeerHistory, style: TextStyle(color: settings.current().mainTextColor())),
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(
value: Provider.of<ContactInfoState>(context).savePeerHistory == "DefaultDeleteHistory"
? AppLocalizations.of(context)!.dontSavePeerHistory

View File

@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/material.dart';
import 'package:cwtch/settings.dart';
import 'package:cwtch/views/torstatusview.dart';
@ -11,6 +12,7 @@ import 'package:provider/provider.dart';
import '../main.dart';
import '../model.dart';
import '../opaque.dart';
import '../torstatus.dart';
import 'addeditprofileview.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())))
]),
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.lock_open),
icon: Icon(CwtchIcons.lock_open_24px),
tooltip: AppLocalizations.of(context)!.tooltipUnlockProfiles,
onPressed: _modalUnlockProfiles,
),

View File

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

View File

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

View File

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

View File

@ -1,10 +1,5 @@
import 'dart:convert';
import 'dart:ffi';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../settings.dart';
final Color malformedColor = Color(0xFFE85DA1);
@ -40,15 +35,10 @@ class MalformedBubbleState extends State<MalformedBubble> {
widthFactor: 1,
child: Padding(
padding: EdgeInsets.all(4),
child: Image(
image: AssetImage("assets/core/broken_heart_24.png"),
filterQuality: FilterQuality.medium,
// 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))),
child: Icon(
CwtchIcons.favorite_black_24dp_broken,
size: 24,
))),
Center(
widthFactor: 1.0,
child: Column(

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../model.dart';
import '../settings.dart';
import 'messagerow.dart';
@ -15,24 +16,44 @@ class _MessageListState extends State<MessageList> {
@override
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(
child: Container(
child: Scrollbar(
isAlwaysShown: true,
controller: ctrlr1,
child: Container(
child: Column(children: [
Visibility(
visible: showMessageWarning,
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...
decoration: BoxDecoration(
image: Provider.of<ContactInfoState>(outerContext).isOnline()
? null
: DecorationImage(
fit: BoxFit.contain,
fit: BoxFit.scaleDown,
alignment: Alignment.center,
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(
controller: ctrlr1,
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) {
var trueIndex = Provider.of<ContactInfoState>(outerContext).totalMessages - index - 1;
return ChangeNotifierProvider(
@ -51,7 +72,7 @@ class _MessageListState extends State<MessageList> {
return RepaintBoundary(child: MessageRow(key: Provider.of<ContactInfoState>(bcontext).getMessageKey(idx)));
});
},
),
))));
))))
])));
}
}

View File

@ -31,10 +31,8 @@ class _MessageRowState extends State<MessageRow> {
fromMe = false;
}
Widget wdgBubble = Flexible(
flex: 3,
fit: FlexFit.loose,
child: malformed ? MalformedBubble() : (Provider.of<MessageState>(context).loaded == true ? widgetForOverlay(Provider.of<MessageState>(context).overlay) : MessageLoadingBubble()));
Widget wdgBubble =
Flexible(flex: 3, fit: FlexFit.loose, child: 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 wdgSpacer = Expanded(child: SizedBox(width: 60, height: 10));
var widgetRow = <Widget>[];
@ -96,7 +94,10 @@ class _MessageRowState extends State<MessageRow> {
final setPeerAttributeJson = jsonEncode(setPeerAttribute);
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);
}
}

View File

@ -1,3 +1,4 @@
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../settings.dart';
@ -5,7 +6,7 @@ import '../settings.dart';
// Provides a styled Password Input Field for use in Form Widgets.
// Callers must provide a text controller, label helper text and a validator.
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 FormFieldValidator validator;
final Function(String)? action;
@ -43,7 +44,7 @@ class _CwtchTextFieldState extends State<CwtchPasswordField> {
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,
color: theme.current().mainTextColor(),
highlightColor: theme.current().defaultButtonColor(),

View File

@ -7,11 +7,12 @@ doNothing(String x) {}
// Provides a styled Text Field for use in Form Widgets.
// Callers must provide a text controller, label helper text and a validator.
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 String labelText;
final FormFieldValidator? validator;
final Function(String) onChanged;
final bool autofocus;
@override
_CwtchTextFieldState createState() => _CwtchTextFieldState();
@ -25,6 +26,7 @@ class _CwtchTextFieldState extends State<CwtchTextField> {
controller: widget.controller,
validator: widget.validator,
onChanged: widget.onChanged,
autofocus: widget.autofocus,
decoration: InputDecoration(
labelText: widget.labelText,
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:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@ -16,13 +17,9 @@ class _TorIconState extends State<TorIcon> {
@override
Widget build(BuildContext context) {
return RepaintBoundary(
child: Image(
image: AssetImage(Provider.of<TorStatus>(context).progress == 0
? "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...
child: Icon(
Provider.of<TorStatus>(context).progress == 0 ? CwtchIcons.onion_off : (Provider.of<TorStatus>(context).progress == 100 ? CwtchIcons.onion_on : CwtchIcons.onion_waiting),
color: Provider.of<Settings>(context).theme.mainTextColor(),
colorBlendMode: BlendMode.srcIn,
semanticLabel: Provider.of<TorStatus>(context).progress == 100
? AppLocalizations.of(context)!.networkStatusOnline
: (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.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <window_size/window_size_plugin.h>

View File

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

View File

@ -21,7 +21,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.0"
version: "2.7.0"
boolean_selector:
dependency: transitive
description:
@ -191,7 +191,7 @@ packages:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.4.0"
nested:
dependency: transitive
description:
@ -392,7 +392,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.0"
version: "0.4.0"
typed_data:
dependency: transitive
description:
@ -450,7 +450,7 @@ packages:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.1"
version: "5.1.2"
sdks:
dart: ">=2.13.0 <3.0.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.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+9
version: 1.0.0+10
environment:
sdk: ">=2.12.0 <3.0.0"
@ -95,6 +95,11 @@ flutter:
- assets/profiles/
- assets/servers/
fonts:
- family: CwtchIcons
fonts:
- asset: assets/fonts/CwtchIcons.ttf
# To add custom fonts to your application, add a fonts section here,
# 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

View File

@ -1,7 +1,7 @@
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)

View File

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

View File

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

View File

@ -52,7 +52,16 @@ END
// Icon with lowest ID value placed first to ensure application icon
// 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
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "com.example" "\0"
VALUE "FileDescription", "A new Flutter project." "\0"
VALUE "CompanyName", "Open Privacy Research Society" "\0"
VALUE "FileDescription", "Cwtch Instant Messenger" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "flutter_app" "\0"
VALUE "LegalCopyright", "Copyright (C) 2021 com.example. All rights reserved." "\0"
VALUE "OriginalFilename", "flutter_app.exe" "\0"
VALUE "ProductName", "flutter_app" "\0"
VALUE "InternalName", "cwtch" "\0"
VALUE "LegalCopyright", "Copyright (C) 2021 Open Privacy Research Society. All rights reserved." "\0"
VALUE "OriginalFilename", "cwtch.exe" "\0"
VALUE "ProductName", "Cwtch" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"
END
END

View File

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

View File

@ -2,7 +2,16 @@
// Microsoft Visual C++ generated include file.
// 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
//

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 "winuser.h"
#include <flutter_windows.h>
@ -70,19 +71,49 @@ WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
const wchar_t* WindowClassRegistrar::GetWindowClass() {
if (!class_registered_) {
WNDCLASS window_class{};
WNDCLASSEX window_class{};
window_class.cbSize = sizeof(WNDCLASSEX);
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
window_class.lpszClassName = kWindowClassName;
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
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 =
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.lpszMenuName = nullptr;
window_class.lpfnWndProc = Win32Window::WndProc;
RegisterClass(&window_class);
RegisterClassEx(&window_class);
class_registered_ = true;
}
return kWindowClassName;