Merge branch 'trunk' of git.openprivacy.ca:cwtch.im/cwtch-ui into filesharing

This commit is contained in:
erinn 2021-09-21 14:57:48 -07:00
commit 4eed72ded3
30 changed files with 318 additions and 119 deletions

View File

@ -20,7 +20,7 @@ steps:
# force by pass of ssh host key check, less secure # force by pass of ssh host key check, less secure
- ssh-keyscan -H git.openprivacy.ca >> ~/.ssh/known_hosts - ssh-keyscan -H git.openprivacy.ca >> ~/.ssh/known_hosts
# use Drone ssh var instead of hardcode to allow forks to build (gogs@git.openprivacy.ca:cwtch.im/cwtch-ui.git) # use Drone ssh var instead of hardcode to allow forks to build (gogs@git.openprivacy.ca:cwtch.im/cwtch-ui.git)
- git clone $DRONE_GIT_SSH_URL . - git clone gogs@git.openprivacy.ca:$DRONE_REPO.git .
- git checkout $DRONE_COMMIT - git checkout $DRONE_COMMIT
- name: fetch - name: fetch
@ -154,7 +154,7 @@ volumes:
temp: {} temp: {}
trigger: trigger:
repo: cwtch.im/cwtch-ui #repo: cwtch.im/cwtch-ui # allow forks to build?
branch: trunk branch: trunk
event: event:
- push - push
@ -187,7 +187,7 @@ steps:
- git init - git init
# -o UserKnownHostsFile=../known_hosts # -o UserKnownHostsFile=../known_hosts
- git config core.sshCommand 'ssh -o StrictHostKeyChecking=no -i ../id_rsa' - git config core.sshCommand 'ssh -o StrictHostKeyChecking=no -i ../id_rsa'
- git remote add origin $Env:DRONE_GIT_SSH_URL - git remote add origin gogs@git.openprivacy.ca:$Env:DRONE_REPO.git
- git pull origin trunk - git pull origin trunk
- git fetch --tags - git fetch --tags
- git checkout $Env:DRONE_COMMIT - git checkout $Env:DRONE_COMMIT
@ -271,7 +271,7 @@ steps:
- scp -r -o StrictHostKeyChecking=no -i id_rsa deploy\\* buildfiles@build.openprivacy.ca:/home/buildfiles/buildfiles/ - scp -r -o StrictHostKeyChecking=no -i id_rsa deploy\\* buildfiles@build.openprivacy.ca:/home/buildfiles/buildfiles/
trigger: trigger:
repo: cwtch.im/cwtch-ui # repo: cwtch.im/cwtch-ui # allow forks to build?
branch: trunk branch: trunk
event: event:
- push - push

6
.gitignore vendored
View File

@ -40,8 +40,10 @@ app.*.symbols
# Obfuscation related # Obfuscation related
app.*.map.json app.*.map.json
libCwtch.so linux/tor
linux/libCwtch.so
android/cwtch/cwtch.aar android/cwtch/cwtch.aar
android/app/src/main/jniLibs/*/libtor.so
coverage coverage
test/failures test/failures
.gradle .gradle

View File

@ -1 +1 @@
v1.2.1-2021-08-30-22-14 v1.2.1-2-ga8e7bba-2021-09-14-21-04

View File

@ -12,35 +12,46 @@ This README covers build instructions, for information on Cwtch itself please go
- `install.home.sh` installs the app into your home directory - `install.home.sh` installs the app into your home directory
- `install.sys.sh` as root to install system wide - `install.sys.sh` as root to install system wide
- or run out of the unziped directory - or run out of the unziped directory
- MacOS: Cwtch.dmg coming soon... - MacOS: Available from [https://cwtch.im/download/](https://cwtch.im/download/) as a .dmg
## Running ## Running
Cwtch processes the following environment variables: Cwtch processes the following environment variables:
- `CWTCH_HOME=` overrides the default storage path of `~/.cwtch` with what ever you choose - `CWTCH_HOME=` overrides the default storage path of `~/.cwtch` with what ever you choose
- `LOG_FILE=` will reroute all of libcwtch-go's logging to the specified file instead of the console - `LOG_FILE=` will reroute all of libcwtch-go's logging to the specified file instead of the console
- `LOG_LEVEL=debug` will set the log level to debug instead of info - `LOG_LEVEL=debug` will set the log level to debug instead of info
## Building ## Building
### Getting Started ### Getting Started
First you will need a valid [flutter sdk installation](https://flutter.dev/docs/get-started/install) First you will need a valid [flutter sdk installation](https://flutter.dev/docs/get-started/install).
and run `flutter pub get` to fetch dependencies.
You will probably want to disable Analytics on the Flutter Tool: `flutter config --no-analytics` You will probably want to disable Analytics on the Flutter Tool: `flutter config --no-analytics`
This project uses the flutter `dev` channel, which you will need to switch to: `flutter channel dev; flutter upgrade`.
Once flutter is set up, run `flutter pub get` from this project folder to fetch dependencies.
By default a development version is built, which loads profiles from `$CWTCH_HOME/dev/`. This is so that you can build
and test development builds with alternative profiles while running a release/stable version of Cwtch uninterrupted.
To build a release version and load normal profiles, use `build-release.sh X` instead of `flutter build X`
### Building on Linux (for Linux) ### Building on Linux (for Linux)
- run `fetch-libcwtch-go.sh`libCwtch-go to fetch a prebuild version of `libCwtch-go.so` go to `./linux`. Include `./linux` in `LD_LIBRARY_PATH` - copy `libCwtch-go.so` to `linux/`, or run `fetch-libcwtch-go.sh` to download it
- run `fetch-tor.sh` and/or ensure that `tor` is in `$PATH` - set `LD_LIBRARY_PATH="$PWD/linux"`
- run `flutter run -d linux` - copy a `tor` binary to `linux/` or run `fetch-tor.sh` to download one
- run `flutter config --enable-linux-desktop` if you've never done so before
- optional: launch cwtch-ui directly by running `flutter run -d linux`
- to build cwtch-ui, run `flutter build linux`
- to package the build, run `linux/package-release.sh`
### Building on Windows (for Windows) ### Building on Windows (for Windows)
- run `fetch-libcwtch-go.ps1` to fetch a prebuild version of `libCwtch.dll` - copy `libCwtch.dll` to `windows/`, or run `fetch-libcwtch-go.ps1` to download it
- run `fetch-tor-win.ps1` to fetch Tor for windows - run `fetch-tor-win.ps1` to fetch Tor for windows
- run `flutter run -d windows` - optional: launch cwtch-ui directly by running `flutter run -d windows`
- to build cwtch-ui, run `flutter build windows`
### Building on Linux/Windows (for Android) ### Building on Linux/Windows (for Android)
@ -49,7 +60,7 @@ You will probably want to disable Analytics on the Flutter Tool: `flutter config
### Building on MacOS ### Building on MacOS
- Navigate to https://git.openprivacy.ca/cwtch.im/libcwtch-go/releases and download the latest libCwtch.dylib into this folder - Navigate to https://git.openprivacy.ca/cwtch.im/libcwtch-go/releases and download the latest libCwtch.dylib into this folder
- Download and install Tor Browser (it's currently the only way to get tor for macos) - Download and install Tor Browser (it's currently the only way to get tor for macos)
- `flutter build macos` - `flutter build macos`
- `./macos/package-release.sh` - `./macos/package-release.sh`

20
build-release.sh Executable file
View File

@ -0,0 +1,20 @@
#!/bin/sh
if [ -z "$1" ]; then
echo "build-release.sh [android|linux|macos|windows]"
exit 1
fi
if [ -f "VERSION" ]; then
VERSION=`cat VERSION`
else
VERSION=`git describe --tags --abbrev=1`
fi
if [ -f "BUILDDATE" ]; then
BUILDDATE=`cat BUILDDATE`
else
BUILDDATE=`date +%G-%m-%d-%H-%M`
fi
flutter build $1 --dart-define BUILD_VER=$VERSION --dart-define BUILD_DATE=$BUILDDATE

View File

@ -307,6 +307,8 @@ class CwtchNotifier {
if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"]) != null) { if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"]) != null) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.isArchived = data["Data"] == "true"; profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.isArchived = data["Data"] == "true";
} }
} else if (data["Key"] == "LastKnowSignature") {
// group syncing information that isn't relevant to the UI...
} else { } else {
EnvironmentConfig.debugLog("unhandled set peer attribute event: ${data['Key']}"); EnvironmentConfig.debugLog("unhandled set peer attribute event: ${data['Key']}");
} }

View File

@ -101,7 +101,7 @@ class CwtchFfi implements Cwtch {
Map<String, String> envVars = Platform.environment; Map<String, String> envVars = Platform.environment;
String cwtchDir = ""; String cwtchDir = "";
if (Platform.isLinux) { if (Platform.isLinux) {
cwtchDir = envVars['CWTCH_HOME'] ?? path.join(envVars['HOME']!, ".cwtch"); cwtchDir = envVars['CWTCH_HOME'] ?? path.join(envVars['HOME']!, ".cwtch");
if (await File("linux/tor").exists()) { if (await File("linux/tor").exists()) {
bundledTor = "linux/tor"; bundledTor = "linux/tor";
} else if (await File("lib/tor").exists()) { } else if (await File("lib/tor").exists()) {
@ -120,8 +120,37 @@ class CwtchFfi implements Cwtch {
cwtchDir = envVars['CWTCH_HOME'] ?? path.join(envVars['HOME']!, "Library/Application Support/Cwtch"); cwtchDir = envVars['CWTCH_HOME'] ?? path.join(envVars['HOME']!, "Library/Application Support/Cwtch");
if (await File("Cwtch.app/Contents/MacOS/Tor/tor.real").exists()) { if (await File("Cwtch.app/Contents/MacOS/Tor/tor.real").exists()) {
bundledTor = "Cwtch.app/Contents/MacOS/Tor/tor.real"; bundledTor = "Cwtch.app/Contents/MacOS/Tor/tor.real";
} else if (await File("/Applications/Cwtch.app/Contents/MacOS/Tor/tor.real").exists()) {
bundledTor = "/Applications/Cwtch.app/Contents/MacOS/Tor/tor.real";
} else if (await File("/Volumes/Cwtch/Cwtch.app/Contents/MacOS/Tor/tor.real").exists()) { } else if (await File("/Volumes/Cwtch/Cwtch.app/Contents/MacOS/Tor/tor.real").exists()) {
bundledTor = "/Volumes/Cwtch/Cwtch.app/Contents/MacOS/Tor/tor.real"; bundledTor = "/Volumes/Cwtch/Cwtch.app/Contents/MacOS/Tor/tor.real";
} else if (await File("/Applications/Tor Browser.app/Contents/MacOS/Tor/tor.real").exists()) {
bundledTor = "/Applications/Tor Browser.app/Contents/MacOS/Tor/tor.real";
print("We couldn't find Tor in the Cwtch app directory, however we can fall back to the Tor Browser binary");
} else {
var splitPath = path.split(dirname(Platform.script.path));
if (splitPath[0] == "/" && splitPath[1] == "Applications") {
var appName = splitPath[2];
print("We're running in /Applications in a non standard app name: $appName");
if (await File("/Applications/$appName/Contents/MacOS/Tor/tor.real").exists()) {
bundledTor = "/Applications/$appName/Contents/MacOS/Tor/tor.real";
}
}
}
}
// the first Cwtch MacOS release (1.2) accidently was a dev build
// we need to temporarily remedy this for a release or two then delete
// if macOs and release build and no profile and is dev profile
// copy dev profile to release profile
if (Platform.isMacOS && EnvironmentConfig.BUILD_VER != dev_version) {
var devProfileExists = await Directory(path.join(cwtchDir, "dev", "profiles")).exists();
var releaseProfileExists = await Directory(path.join(cwtchDir, "profiles")).exists();
if (devProfileExists && !releaseProfileExists) {
print("MacOS one time dev -> release profile migration...");
await Process.run("cp", ["-r", "-p", path.join(cwtchDir, "dev", "profiles"), cwtchDir]);
await Process.run("cp", ["-r", "-p", path.join(cwtchDir, "dev", "SALT"), cwtchDir]);
await Process.run("cp", ["-r", "-p", path.join(cwtchDir, "dev", "ui.globals"), cwtchDir]);
} }
} }
@ -462,7 +491,6 @@ class CwtchFfi implements Cwtch {
malloc.free(u2); malloc.free(u2);
} }
@override @override
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
void UpdateMessageFlags(String profile, String handle, int index, int flags) { void UpdateMessageFlags(String profile, String handle, int index, int flags) {

View File

@ -1,6 +1,19 @@
{ {
"@@locale": "de", "@@locale": "de",
"@@last_modified": "2021-08-29T18:35:41+02:00", "@@last_modified": "2021-09-21T23:09:19+02:00",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.",
"settingFileSharing": "File Sharing",
"tooltipSendFile": "Send File",
"messageFileOffered": "Contact is offering to send you a file",
"messageFileSent": "You sent a file",
"messageEnableFileSharing": "Enable the file sharing experiment to view this message.",
"labelFilesize": "Size",
"labelFilename": "Filename",
"downloadFileButton": "Download",
"openFolderButton": "Open Folder",
"retrievingManifestMessage": "Retrieving file information...",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions",
"streamerModeLabel": "Streamer\/Presentation Mode",
"archiveConversation": "Archive this Conversation", "archiveConversation": "Archive this Conversation",
"profileOnionLabel": "Senden Sie diese Adresse an Peers, mit denen Sie sich verbinden möchten", "profileOnionLabel": "Senden Sie diese Adresse an Peers, mit denen Sie sich verbinden möchten",
"addPeerTab": "Einen anderen Nutzer hinzufügen", "addPeerTab": "Einen anderen Nutzer hinzufügen",

View File

@ -1,6 +1,19 @@
{ {
"@@locale": "en", "@@locale": "en",
"@@last_modified": "2021-08-29T18:35:41+02:00", "@@last_modified": "2021-09-21T23:09:19+02:00",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.",
"settingFileSharing": "File Sharing",
"tooltipSendFile": "Send File",
"messageFileOffered": "Contact is offering to send you a file",
"messageFileSent": "You sent a file",
"messageEnableFileSharing": "Enable the file sharing experiment to view this message.",
"labelFilesize": "Size",
"labelFilename": "Filename",
"downloadFileButton": "Download",
"openFolderButton": "Open Folder",
"retrievingManifestMessage": "Retrieving file information...",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions",
"streamerModeLabel": "Streamer\/Presentation Mode",
"archiveConversation": "Archive this Conversation", "archiveConversation": "Archive this Conversation",
"profileOnionLabel": "Send this address to people you want to connect with", "profileOnionLabel": "Send this address to people you want to connect with",
"addPeerTab": "Add a contact", "addPeerTab": "Add a contact",

View File

@ -1,6 +1,19 @@
{ {
"@@locale": "es", "@@locale": "es",
"@@last_modified": "2021-08-29T18:35:41+02:00", "@@last_modified": "2021-09-21T23:09:19+02:00",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.",
"settingFileSharing": "File Sharing",
"tooltipSendFile": "Send File",
"messageFileOffered": "Contact is offering to send you a file",
"messageFileSent": "You sent a file",
"messageEnableFileSharing": "Enable the file sharing experiment to view this message.",
"labelFilesize": "Size",
"labelFilename": "Filename",
"downloadFileButton": "Download",
"openFolderButton": "Open Folder",
"retrievingManifestMessage": "Retrieving file information...",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions",
"streamerModeLabel": "Streamer\/Presentation Mode",
"archiveConversation": "Archive this Conversation", "archiveConversation": "Archive this Conversation",
"profileOnionLabel": "Envía esta dirección a los contactos con los que quieras conectarte", "profileOnionLabel": "Envía esta dirección a los contactos con los que quieras conectarte",
"addPeerTab": "Agregar Contacto", "addPeerTab": "Agregar Contacto",

View File

@ -1,6 +1,19 @@
{ {
"@@locale": "fr", "@@locale": "fr",
"@@last_modified": "2021-08-29T18:35:41+02:00", "@@last_modified": "2021-09-21T23:09:19+02:00",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.",
"settingFileSharing": "File Sharing",
"tooltipSendFile": "Send File",
"messageFileOffered": "Contact is offering to send you a file",
"messageFileSent": "You sent a file",
"messageEnableFileSharing": "Enable the file sharing experiment to view this message.",
"labelFilesize": "Size",
"labelFilename": "Filename",
"downloadFileButton": "Download",
"openFolderButton": "Open Folder",
"retrievingManifestMessage": "Retrieving file information...",
"descriptionStreamerMode": "Si elle est activée, cette option donne un rendu visuel plus privé à l'application pour la diffusion ou la présentation, par exemple en masquant les profils et les contacts.",
"streamerModeLabel": "Mode Streamer\/Présentation",
"archiveConversation": "Archiver cette conversation", "archiveConversation": "Archiver cette conversation",
"profileOnionLabel": "Envoyez cette adresse aux personnes avec lesquelles vous souhaitez entrer en contact.", "profileOnionLabel": "Envoyez cette adresse aux personnes avec lesquelles vous souhaitez entrer en contact.",
"addPeerTab": "Ajouter un contact", "addPeerTab": "Ajouter un contact",

View File

@ -1,6 +1,19 @@
{ {
"@@locale": "it", "@@locale": "it",
"@@last_modified": "2021-08-29T18:35:41+02:00", "@@last_modified": "2021-09-21T23:09:19+02:00",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.",
"settingFileSharing": "File Sharing",
"tooltipSendFile": "Send File",
"messageFileOffered": "Contact is offering to send you a file",
"messageFileSent": "You sent a file",
"messageEnableFileSharing": "Enable the file sharing experiment to view this message.",
"labelFilesize": "Size",
"labelFilename": "Filename",
"downloadFileButton": "Download",
"openFolderButton": "Open Folder",
"retrievingManifestMessage": "Retrieving file information...",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions",
"streamerModeLabel": "Streamer\/Presentation Mode",
"archiveConversation": "Archive this Conversation", "archiveConversation": "Archive this Conversation",
"profileOnionLabel": "Inviare questo indirizzo ai peer con cui si desidera connettersi", "profileOnionLabel": "Inviare questo indirizzo ai peer con cui si desidera connettersi",
"addPeerTab": "Aggiungi un peer", "addPeerTab": "Aggiungi un peer",

View File

@ -1,6 +1,19 @@
{ {
"@@locale": "pl", "@@locale": "pl",
"@@last_modified": "2021-08-29T18:35:41+02:00", "@@last_modified": "2021-09-21T23:09:19+02:00",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.",
"settingFileSharing": "File Sharing",
"tooltipSendFile": "Send File",
"messageFileOffered": "Contact is offering to send you a file",
"messageFileSent": "You sent a file",
"messageEnableFileSharing": "Enable the file sharing experiment to view this message.",
"labelFilesize": "Size",
"labelFilename": "Filename",
"downloadFileButton": "Download",
"openFolderButton": "Open Folder",
"retrievingManifestMessage": "Retrieving file information...",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions",
"streamerModeLabel": "Streamer\/Presentation Mode",
"archiveConversation": "Archive this Conversation", "archiveConversation": "Archive this Conversation",
"profileOnionLabel": "Send this address to contacts you want to connect with", "profileOnionLabel": "Send this address to contacts you want to connect with",
"addPeerTab": "Add a contact", "addPeerTab": "Add a contact",

View File

@ -1,6 +1,19 @@
{ {
"@@locale": "pt", "@@locale": "pt",
"@@last_modified": "2021-08-29T18:35:41+02:00", "@@last_modified": "2021-09-21T23:09:19+02:00",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.",
"settingFileSharing": "File Sharing",
"tooltipSendFile": "Send File",
"messageFileOffered": "Contact is offering to send you a file",
"messageFileSent": "You sent a file",
"messageEnableFileSharing": "Enable the file sharing experiment to view this message.",
"labelFilesize": "Size",
"labelFilename": "Filename",
"downloadFileButton": "Download",
"openFolderButton": "Open Folder",
"retrievingManifestMessage": "Retrieving file information...",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions",
"streamerModeLabel": "Streamer\/Presentation Mode",
"archiveConversation": "Archive this Conversation", "archiveConversation": "Archive this Conversation",
"profileOnionLabel": "Send this address to contacts you want to connect with", "profileOnionLabel": "Send this address to contacts you want to connect with",
"addPeerTab": "Add a contact", "addPeerTab": "Add a contact",

View File

@ -156,7 +156,7 @@ class FlwtchState extends State<Flwtch> {
Future.delayed(Duration(seconds: 2)).then((value) { Future.delayed(Duration(seconds: 2)).then((value) {
if (Platform.isAndroid) { if (Platform.isAndroid) {
SystemNavigator.pop(); SystemNavigator.pop();
} else if (Platform.isLinux || Platform.isWindows) { } else if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) {
print("Exiting..."); print("Exiting...");
exit(0); exit(0);
} }

View File

@ -32,6 +32,7 @@ class AppState extends ChangeNotifier {
String? _selectedProfile; String? _selectedProfile;
String? _selectedConversation; String? _selectedConversation;
int _initialScrollIndex = 0; int _initialScrollIndex = 0;
int _hoveredIndex = -1;
int? _selectedIndex; int? _selectedIndex;
bool _unreadMessagesBelow = false; bool _unreadMessagesBelow = false;
@ -63,6 +64,14 @@ class AppState extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
// Never use this for message lookup - can be a non-indexed value
// e.g. -1
int get hoveredIndex => _hoveredIndex;
set hoveredIndex(int newVal) {
this._hoveredIndex = newVal;
notifyListeners();
}
bool get unreadMessagesBelow => _unreadMessagesBelow; bool get unreadMessagesBelow => _unreadMessagesBelow;
set unreadMessagesBelow(bool newVal) { set unreadMessagesBelow(bool newVal) {
this._unreadMessagesBelow = newVal; this._unreadMessagesBelow = newVal;
@ -442,21 +451,18 @@ class ContactInfoState extends ChangeNotifier {
String? _server; String? _server;
late bool _archived; late bool _archived;
ContactInfoState( ContactInfoState(this.profileOnion, this.onion,
this.profileOnion, {nickname = "",
this.onion, { isGroup = false,
nickname = "", authorization = ContactAuthorization.unknown,
isGroup = false, status = "",
authorization = ContactAuthorization.unknown, imagePath = "",
status = "", savePeerHistory = "DeleteHistoryConfirmed",
imagePath = "", numMessages = 0,
savePeerHistory = "DeleteHistoryConfirmed", numUnread = 0,
numMessages = 0, lastMessageTime,
numUnread = 0, server,
lastMessageTime, archived = false}) {
server,
archived = false
}) {
this._nickname = nickname; this._nickname = nickname;
this._isGroup = isGroup; this._isGroup = isGroup;
this._authorization = authorization; this._authorization = authorization;
@ -482,8 +488,8 @@ class ContactInfoState extends ChangeNotifier {
this._archived = archived; this._archived = archived;
notifyListeners(); notifyListeners();
} }
bool get isArchived => this._archived;
bool get isArchived => this._archived;
set savePeerHistory(String newVal) { set savePeerHistory(String newVal) {
this._savePeerHistory = newVal; this._savePeerHistory = newVal;

View File

@ -630,7 +630,7 @@ class OpaqueLight extends OpaqueThemeType {
static final Color whitePurple = Color(0xFFFFFDFF); static final Color whitePurple = Color(0xFFFFFDFF);
static final Color softPurple = Color(0xFFFDF3FC); static final Color softPurple = Color(0xFFFDF3FC);
static final Color purple = Color(0xFFDFB9DE); static final Color purple = Color(0xFFDFB9DE);
static final Color brightPurple = Color(0xFF760388); static final Color brightPurple = Color(0xFFD1B0E0);
static final Color darkPurple = Color(0xFF350052); static final Color darkPurple = Color(0xFF350052);
static final Color greyPurple = Color(0xFF775F84); static final Color greyPurple = Color(0xFF775F84);
static final Color pink = Color(0xFFE85DA1); static final Color pink = Color(0xFFE85DA1);
@ -900,11 +900,11 @@ class OpaqueLight extends OpaqueThemeType {
} }
Color messageFromMeBackgroundColor() { Color messageFromMeBackgroundColor() {
return darkPurple; return brightPurple;
} }
Color messageFromMeTextColor() { Color messageFromMeTextColor() {
return whitePurple; return mainTextColor();
} }
Color messageFromOtherBackgroundColor() { Color messageFromOtherBackgroundColor() {
@ -948,11 +948,14 @@ ThemeData mkThemeData(Settings opaque) {
backgroundColor: opaque.current().backgroundMainColor(), backgroundColor: opaque.current().backgroundMainColor(),
highlightColor: opaque.current().hilightElementTextColor(), highlightColor: opaque.current().hilightElementTextColor(),
iconTheme: IconThemeData( iconTheme: IconThemeData(
color: opaque.current().mainTextColor(), color: opaque.current().toolbarIconColor(),
), ),
cardColor: opaque.current().backgroundMainColor(), cardColor: opaque.current().backgroundMainColor(),
appBarTheme: AppBarTheme( appBarTheme: AppBarTheme(
backgroundColor: opaque.current().backgroundPaneColor(), backgroundColor: opaque.current().backgroundPaneColor(),
iconTheme: IconThemeData(
color: opaque.current().mainTextColor(),
),
titleTextStyle: TextStyle( titleTextStyle: TextStyle(
color: opaque.current().mainTextColor(), color: opaque.current().mainTextColor(),
), ),

View File

@ -32,6 +32,7 @@ class Settings extends ChangeNotifier {
DualpaneMode _uiColumnModeLandscape = DualpaneMode.CopyPortrait; DualpaneMode _uiColumnModeLandscape = DualpaneMode.CopyPortrait;
bool blockUnknownConnections = false; bool blockUnknownConnections = false;
bool streamerMode = false;
/// Set the dark theme. /// Set the dark theme.
void setDark() { void setDark() {
@ -75,11 +76,11 @@ class Settings extends ChangeNotifier {
// Set Locale and notify listeners // Set Locale and notify listeners
switchLocale(Locale(settings["Locale"])); switchLocale(Locale(settings["Locale"]));
// Decide whether to enable Experiments blockUnknownConnections = settings["BlockUnknownConnections"] ?? false;
blockUnknownConnections = settings["BlockUnknownConnections"]; streamerMode = settings["StreamerMode"] ?? false;
// Decide whether to enable Experiments // Decide whether to enable Experiments
experimentsEnabled = settings["ExperimentsEnabled"]; experimentsEnabled = settings["ExperimentsEnabled"] ?? false;
// Set the internal experiments map. Casting from the Map<dynamic, dynamic> that we get from JSON // Set the internal experiments map. Casting from the Map<dynamic, dynamic> that we get from JSON
experiments = new HashMap<String, bool>.from(settings["Experiments"]); experiments = new HashMap<String, bool>.from(settings["Experiments"]);
@ -106,6 +107,11 @@ class Settings extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
setStreamerMode(bool newSteamerMode) {
streamerMode = newSteamerMode;
notifyListeners();
}
/// Block Unknown Connections will autoblock connections if they authenticate with public key not in our contacts list. /// Block Unknown Connections will autoblock connections if they authenticate with public key not in our contacts list.
/// This is one of the best tools we have to combat abuse, while it isn't ideal it does allow a user to curate their contacts /// This is one of the best tools we have to combat abuse, while it isn't ideal it does allow a user to curate their contacts
/// list without being bothered by spurious requests (either permanently, or as a short term measure). /// list without being bothered by spurious requests (either permanently, or as a short term measure).
@ -228,6 +234,7 @@ class Settings extends ChangeNotifier {
"Theme": themeString, "Theme": themeString,
"PreviousPid": -1, "PreviousPid": -1,
"BlockUnknownConnections": blockUnknownConnections, "BlockUnknownConnections": blockUnknownConnections,
"StreamerMode": streamerMode,
"ExperimentsEnabled": this.experimentsEnabled, "ExperimentsEnabled": this.experimentsEnabled,
"Experiments": experiments, "Experiments": experiments,
"StateRootPane": 0, "StateRootPane": 0,

View File

@ -30,6 +30,7 @@ void selectConversation(BuildContext context, String handle) {
Provider.of<AppState>(context, listen: false).initialScrollIndex = initialIndex; Provider.of<AppState>(context, listen: false).initialScrollIndex = initialIndex;
Provider.of<AppState>(context, listen: false).selectedConversation = handle; Provider.of<AppState>(context, listen: false).selectedConversation = handle;
Provider.of<AppState>(context, listen: false).selectedIndex = null; Provider.of<AppState>(context, listen: false).selectedIndex = null;
Provider.of<AppState>(context, listen: false).hoveredIndex = -1;
// if in singlepane mode, push to the stack // if in singlepane mode, push to the stack
var isLandscape = Provider.of<AppState>(context, listen: false).isLandscape(context); var isLandscape = Provider.of<AppState>(context, listen: false).isLandscape(context);
if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(context, handle); if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(context, handle);

View File

@ -137,6 +137,19 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
inactiveTrackColor: settings.theme.defaultButtonDisabledColor(), inactiveTrackColor: settings.theme.defaultButtonDisabledColor(),
secondary: Icon(CwtchIcons.block_unknown, color: settings.current().mainTextColor()), secondary: Icon(CwtchIcons.block_unknown, color: settings.current().mainTextColor()),
), ),
SwitchListTile(
title: Text(AppLocalizations.of(context)!.streamerModeLabel, style: TextStyle(color: settings.current().mainTextColor())),
subtitle: Text(AppLocalizations.of(context)!.descriptionStreamerMode),
value: settings.streamerMode,
onChanged: (bool value) {
settings.setStreamerMode(value);
// Save Settings...
saveSettings(context);
},
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())),
subtitle: Text(AppLocalizations.of(context)!.descriptionExperiments), subtitle: Text(AppLocalizations.of(context)!.descriptionExperiments),

View File

@ -162,11 +162,12 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
onPressed: () { onPressed: () {
showAlertDialog(context); showAlertDialog(context);
}, },
style: ButtonStyle ( style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.transparent)),
backgroundColor: MaterialStateProperty.all(Colors.transparent)
),
icon: Icon(CwtchIcons.leave_group), icon: Icon(CwtchIcons.leave_group),
label: Text(AppLocalizations.of(context)!.leaveGroup, style: TextStyle(decoration: TextDecoration.underline),), label: Text(
AppLocalizations.of(context)!.leaveGroup,
style: TextStyle(decoration: TextDecoration.underline),
),
)) ))
]) ])
]) ])

View File

@ -253,7 +253,7 @@ class _MessageViewState extends State<MessageView> {
focusedBorder: InputBorder.none, focusedBorder: InputBorder.none,
enabled: true, enabled: true,
suffixIcon: ElevatedButton( suffixIcon: ElevatedButton(
child: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of<Settings>(context).theme.mainTextColor()), child: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of<Settings>(context).theme.defaultButtonTextColor()),
onPressed: isOffline ? null : _sendMessage, onPressed: isOffline ? null : _sendMessage,
))), ))),
)))), )))),
@ -274,18 +274,27 @@ class _MessageViewState extends State<MessageView> {
color: message.getMetadata().senderHandle != Provider.of<AppState>(context).selectedProfile color: message.getMetadata().senderHandle != Provider.of<AppState>(context).selectedProfile
? Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor() ? Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor()
: Provider.of<Settings>(context).theme.messageFromMeBackgroundColor(), : Provider.of<Settings>(context).theme.messageFromMeBackgroundColor(),
child: Wrap(runAlignment: WrapAlignment.spaceEvenly, alignment: WrapAlignment.spaceEvenly, runSpacing: 1.0, crossAxisAlignment: WrapCrossAlignment.center, children: [ child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Center(widthFactor: 1, child: Padding(padding: EdgeInsets.all(10.0), child: Icon(Icons.reply, size: 32))), Stack(children: [
Center(widthFactor: 1.0, child: message.getPreviewWidget(context)), Align(
Center( alignment: Alignment.topRight,
widthFactor: 1.0, child: IconButton(
child: IconButton( icon: Icon(Icons.highlight_remove),
icon: Icon(Icons.highlight_remove), tooltip: AppLocalizations.of(context)!.tooltipRemoveThisQuotedMessage,
tooltip: AppLocalizations.of(context)!.tooltipRemoveThisQuotedMessage, onPressed: () {
onPressed: () { Provider.of<AppState>(context, listen: false).selectedIndex = null;
Provider.of<AppState>(context, listen: false).selectedIndex = null; },
}, )),
)) Align(
alignment: Alignment.topLeft,
child: Padding(padding: EdgeInsets.all(2.0), child: Icon(Icons.reply)),
)
]),
Wrap(
runAlignment: WrapAlignment.spaceEvenly,
alignment: WrapAlignment.center,
runSpacing: 1.0,
children: [Center(widthFactor: 1.0, child: Padding(padding: EdgeInsets.all(10.0), child: message.getPreviewWidget(context)))]),
])); ]));
} else { } else {
return MessageLoadingBubble(); return MessageLoadingBubble();

View File

@ -51,6 +51,7 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
Icon( Icon(
CwtchIcons.cwtch_knott, CwtchIcons.cwtch_knott,
size: 36, size: 36,
color: settings.theme.mainTextColor(),
), ),
SizedBox( SizedBox(
width: 10, width: 10,

View File

@ -65,8 +65,11 @@ class _ContactRowState extends State<ContactRow> {
child: LinearProgressIndicator( child: LinearProgressIndicator(
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(), color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
)), )),
Text(contact.onion, Visibility(
style: TextStyle(color: contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedTextColor() : Provider.of<Settings>(context).theme.mainTextColor())), visible: !Provider.of<Settings>(context).streamerMode,
child: Text(contact.onion,
style: TextStyle(color: contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedTextColor() : Provider.of<Settings>(context).theme.mainTextColor())),
)
], ],
))), ))),
Padding( Padding(

View File

@ -56,44 +56,44 @@ class _MessageListState extends State<MessageList> {
Text("")), Text("")),
))), ))),
Expanded( Expanded(
child: Container( 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.scaleDown, fit: BoxFit.scaleDown,
alignment: Alignment.center, 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.hilightElementTextColor(), BlendMode.srcIn))), colorFilter: ColorFilter.mode(Provider.of<Settings>(context).theme.hilightElementTextColor(), BlendMode.srcIn))),
// Don't load messages for syncing server... // Don't load messages for syncing server...
child: loadMessages child: loadMessages
? ScrollablePositionedList.builder( ? ScrollablePositionedList.builder(
itemPositionsListener: widget.scrollListener, itemPositionsListener: widget.scrollListener,
itemScrollController: widget.scrollController, itemScrollController: widget.scrollController,
initialScrollIndex: Provider.of<AppState>(outerContext, listen: false).initialScrollIndex, initialScrollIndex: Provider.of<AppState>(outerContext, listen: false).initialScrollIndex,
itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages, itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages,
reverse: true, // NOTE: There seems to be a bug in flutter that corrects the mouse wheel scroll, but not the drag direction... 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 profileOnion = Provider.of<ProfileInfoState>(outerContext, listen: false).onion; var profileOnion = Provider.of<ProfileInfoState>(outerContext, listen: false).onion;
var contactHandle = Provider.of<ContactInfoState>(outerContext, listen: false).onion; var contactHandle = Provider.of<ContactInfoState>(outerContext, listen: false).onion;
var messageIndex = Provider.of<ContactInfoState>(outerContext).totalMessages - index - 1; var messageIndex = Provider.of<ContactInfoState>(outerContext).totalMessages - index - 1;
return FutureBuilder( return FutureBuilder(
future: messageHandler(outerContext, profileOnion, contactHandle, messageIndex), future: messageHandler(outerContext, profileOnion, contactHandle, messageIndex),
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData) { if (snapshot.hasData) {
var message = snapshot.data as Message; var message = snapshot.data as Message;
// Already includes MessageRow,, // Already includes MessageRow,,
return message.getWidget(context); return message.getWidget(context);
} else { } else {
return MessageLoadingBubble(); return MessageLoadingBubble();
} }
},
);
}, },
) );
: null)) },
)
: null))
]))); ])));
} }
} }

View File

@ -22,7 +22,6 @@ class MessageRow extends StatefulWidget {
} }
class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMixin { class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMixin {
bool showMenu = false;
bool showBlockedMessage = false; bool showBlockedMessage = false;
late AnimationController _controller; late AnimationController _controller;
late Animation<Alignment> _animation; late Animation<Alignment> _animation;
@ -70,7 +69,7 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
} }
Widget wdgIcons = Visibility( Widget wdgIcons = Visibility(
visible: this.showMenu, visible: Provider.of<AppState>(context).hoveredIndex == Provider.of<MessageMetadata>(context).messageIndex,
maintainSize: true, maintainSize: true,
maintainAnimation: true, maintainAnimation: true,
maintainState: true, maintainState: true,
@ -164,12 +163,12 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
// For desktop... // For desktop...
onHover: (event) { onHover: (event) {
setState(() { setState(() {
this.showMenu = true; Provider.of<AppState>(context, listen: false).hoveredIndex = Provider.of<MessageMetadata>(context).messageIndex;
}); });
}, },
onExit: (event) { onExit: (event) {
setState(() { setState(() {
this.showMenu = false; Provider.of<AppState>(context, listen: false).hoveredIndex = -1;
}); });
}, },
child: GestureDetector( child: GestureDetector(

View File

@ -46,12 +46,14 @@ class _ProfileRowState extends State<ProfileRow> {
softWrap: true, softWrap: true,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
ExcludeSemantics( Visibility(
child: Text( visible: !Provider.of<Settings>(context).streamerMode,
profile.onion, child: ExcludeSemantics(
softWrap: true, child: Text(
overflow: TextOverflow.ellipsis, profile.onion,
)) softWrap: true,
overflow: TextOverflow.ellipsis,
)))
], ],
)), )),
IconButton( IconButton(

View File

@ -1,3 +1,3 @@
#!/bin/sh #!/bin/sh
env LD_LIBRARY_PATH=./lib/ ./lib/cwtch exec env LD_LIBRARY_PATH=./lib/ ./lib/cwtch

View File

@ -1,3 +1,3 @@
#!/bin/sh #!/bin/sh
env LD_LIBRARY_PATH=~/.local/lib/cwtch/ ~/.local/lib/cwtch/cwtch exec env LD_LIBRARY_PATH=~/.local/lib/cwtch/ ~/.local/lib/cwtch/cwtch

View File

@ -1,3 +1,3 @@
#!/bin/sh #!/bin/sh
env LD_LIBRARY_PATH=/usr/lib/cwtch /usr/lib/cwtch/cwtch exec env LD_LIBRARY_PATH=/usr/lib/cwtch /usr/lib/cwtch/cwtch