Compare commits

...

65 Commits
torUp ... trunk

Author SHA1 Message Date
Dan Ballard bfae3b577f final fixes for windows package
continuous-integration/drone/push Build is passing Details
2021-06-30 09:46:22 -07:00
Dan Ballard b20a82fb1a nsis installer now tested on windows
continuous-integration/drone/push Build is failing Details
2021-06-29 19:39:24 -07:00
Dan Ballard 302e504d76 add LICENSE for build steps nsis
continuous-integration/drone/push Build is failing Details
2021-06-29 19:17:03 -07:00
Dan Ballard 00c77c97d2 updated nsis installer script
continuous-integration/drone/push Build is failing Details
2021-06-29 19:07:19 -07:00
Dan Ballard d12efa2204 Merge branch 'trunk' of git.openprivacy.ca:flutter/flutter_app into trunk
continuous-integration/drone/push Build is failing Details
2021-06-29 17:26:04 -07:00
Dan Ballard ee10637261 drone test nsis installer 2021-06-29 16:20:54 -07:00
Sarah Jamie Lewis 3cf87e825b Update 'README.md'
continuous-integration/drone/push Build is passing Details
2021-06-25 07:38:05 -07:00
Dan Ballard 96d9090b1d wow Get-FileHash is a truely garbage cmdlet
continuous-integration/drone/push Build is passing Details
2021-06-25 00:56:24 -07:00
Dan Ballard ca58e063b6 guess i m tired and dumb
continuous-integration/drone/push Build is failing Details
2021-06-25 00:45:14 -07:00
Dan Ballard 2d537df810 signtool syntax dumbness
continuous-integration/drone/push Build is failing Details
2021-06-25 00:35:09 -07:00
Dan Ballard 94117e88d2 signtool dumb path
continuous-integration/drone/push Build is failing Details
2021-06-25 00:24:42 -07:00
Dan Ballard 034a856c05 and so the journy of variables being syntax errors begins
continuous-integration/drone/push Build is failing Details
2021-06-24 22:39:29 -07:00
Dan Ballard 69c748dcc4 Merge branch 'trunk' of git.openprivacy.ca:flutter/flutter_app into trunk
continuous-integration/drone/push Build is failing Details
2021-06-24 22:34:24 -07:00
Dan Ballard 9ae38f6870 sign the exe 2021-06-24 22:34:17 -07:00
Dan Ballard 97c4deb4bf Merge pull request 'Remove todos + show/hide password translations' (#218) from launch into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #218
2021-06-24 14:48:15 -07:00
Sarah Jamie Lewis 8d6d5a23b9 Merge branch 'trunk' into launch
continuous-integration/drone/pr Build is passing Details
2021-06-24 14:38:33 -07:00
Sarah Jamie Lewis 9a6dbae0c4 Remove todos + show/hide password translations
continuous-integration/drone/pr Build is passing Details
2021-06-24 14:37:19 -07:00
erinn 6eccece360 Merge pull request 'Add Syncing Warning for Groups' (#217) from launch into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #217
2021-06-24 14:05:43 -07:00
Sarah Jamie Lewis d538d8442e Merge branch 'trunk' into launch
continuous-integration/drone/pr Build is passing Details
2021-06-24 14:05:03 -07:00
Sarah Jamie Lewis 62ef3549b5 Add Syncing Warning for Groups
continuous-integration/drone/pr Build is passing Details
2021-06-24 13:38:41 -07:00
Dan Ballard e33a543e66 Merge pull request 'Add Flaticons Licenses, Fix Splash Screen, Fix Android License Prompt' (#216) from launch into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #216
2021-06-24 13:07:20 -07:00
Sarah Jamie Lewis 0090e86ade Add Flaticons Licenses, Fix Splash Screen, Fix Android License Prompt
continuous-integration/drone/pr Build is passing Details
2021-06-24 12:57:48 -07:00
Dan Ballard 82fe74937c pubspec bump
continuous-integration/drone/push Build is passing Details
2021-06-24 12:20:05 -07:00
Dan Ballard 23b0af77f1 Merge pull request 'Experimental Check on Group Invitations + Delete Profile Fixes for Passwordless Profiles' (#214) from launch into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #214
2021-06-24 12:07:50 -07:00
Sarah Jamie Lewis dad995cd3c Add Contact Header Image + Contact Selection
continuous-integration/drone/pr Build is passing Details
2021-06-24 11:48:18 -07:00
Sarah Jamie Lewis 17518b1e6d Bump libcwtch-go version
continuous-integration/drone/pr Build is passing Details
2021-06-24 11:26:16 -07:00
Sarah Jamie Lewis 0c80577f72 Hide "Current Password" field for password-less accounts 2021-06-24 11:26:16 -07:00
Sarah Jamie Lewis a47a17b3a9 Experimental Check on Group Invitations 2021-06-24 11:26:16 -07:00
Dan Ballard 5ca0712d5e fix tests, rename themes from cwtch* to opaque* 2021-06-24 11:26:16 -07:00
Dan Ballard b61f59643f contacts that are blocked get blocked colors; rework opaque theming a little, start using for font size 2021-06-24 11:26:14 -07:00
erinn 82ee06d840 dual pane wiring 2021-06-24 11:24:50 -07:00
erinn eb12f57135 dualpane settings wiring 2021-06-24 11:24:50 -07:00
Sarah Jamie Lewis ddb671cf6a Merge pull request 'contacts that are blocked get blocked colors; rework opaque theming a little, start using for font size' (#200) from opaqueBlock into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #200
2021-06-24 11:20:48 -07:00
Sarah Jamie Lewis 93dbba5f15 Merge branch 'trunk' into opaqueBlock
continuous-integration/drone/pr Build is passing Details
2021-06-24 11:20:34 -07:00
Dan Ballard 1c961c2c4c fix tests, rename themes from cwtch* to opaque*
continuous-integration/drone/pr Build is passing Details
2021-06-24 10:59:23 -07:00
Dan Ballard 040bb585a1 drone windows cmd/ps syntax is corrosive to brains
continuous-integration/drone/push Build is passing Details
2021-06-24 01:31:41 -07:00
Dan Ballard e89c4bdb0e drone windows cmd/ps syntax is corrosive to brains
continuous-integration/drone/push Build is failing Details
2021-06-24 01:21:27 -07:00
Dan Ballard f8e5bd29f3 drone windows cmd/ps syntax is toxic to brains
continuous-integration/drone/push Build was killed Details
2021-06-24 01:15:35 -07:00
Dan Ballard a18a522f81 drone windows beyond terrible
continuous-integration/drone/push Build is failing Details
2021-06-24 01:04:34 -07:00
Dan Ballard 16ce38da75 drone windows hyper dumb
continuous-integration/drone/push Build is failing Details
2021-06-24 00:51:06 -07:00
Dan Ballard 126c68e185 drone windows hyper dumb
continuous-integration/drone/push Build is failing Details
2021-06-24 00:40:16 -07:00
Dan Ballard c533580b70 drone windows hyper dumb
continuous-integration/drone/push Build is failing Details
2021-06-24 00:26:43 -07:00
Dan Ballard ca303ba41c drone windows hyper dumb
continuous-integration/drone/push Build is failing Details
2021-06-24 00:13:27 -07:00
Dan Ballard ff4a8a02ab drone windows hyper dumb
continuous-integration/drone/push Build is failing Details
2021-06-24 00:01:29 -07:00
Dan Ballard 6c0c31a74f drone windows is dumb
continuous-integration/drone/push Build is failing Details
2021-06-23 23:50:55 -07:00
Dan Ballard 4e0be20930 drone windows is dumb
continuous-integration/drone/push Build is failing Details
2021-06-23 23:27:35 -07:00
Dan Ballard 0a93dd40b6 drone windows compress archive is dumb
continuous-integration/drone/push Build is failing Details
2021-06-23 23:16:35 -07:00
Dan Ballard 5f6008a152 drone windows msix debug
continuous-integration/drone/push Build is failing Details
2021-06-23 23:03:22 -07:00
Dan Ballard 901293377e drone windows msix debug
continuous-integration/drone/push Build is failing Details
2021-06-23 22:41:05 -07:00
Dan Ballard a54d50a642 drone window: rework order, variables, so dlls and tor are in msix; remove pub pligin window_size
continuous-integration/drone/push Build is failing Details
2021-06-23 22:18:31 -07:00
Dan Ballard f7c30755d0 remove pub pligin window_size
continuous-integration/drone/push Build is passing Details
2021-06-23 21:15:35 -07:00
Dan Ballard 66568a7983 drone windows msix do pubspec.yaml sub before flutter get
continuous-integration/drone/push Build is failing Details
2021-06-23 18:47:52 -07:00
Dan Ballard da8a35458f drone windows msix powershell Set-Content for utf not >
continuous-integration/drone/push Build is failing Details
2021-06-23 18:35:36 -07:00
Dan Ballard ae2ae51d2d drone windows msix powershell cant read and pipe write to the same file...
continuous-integration/drone/push Build is failing Details
2021-06-23 18:10:52 -07:00
Dan Ballard b7a4486536 Merge branch 'trunk' of git.openprivacy.ca:flutter/flutter_app into trunk
continuous-integration/drone/push Build is failing Details
2021-06-23 17:57:01 -07:00
Dan Ballard 3e56f8c917 drone windows msix 2021-06-23 17:56:56 -07:00
erinn c4203477de Merge pull request 'Fix Reconnect Woes + DeleteProfile on Android' (#206) from beta_fixes into trunk
continuous-integration/drone/push Build is passing Details
Reviewed-on: #206
2021-06-23 15:02:47 -07:00
erinn 1c1a1ec68d Merge branch 'trunk' into beta_fixes
continuous-integration/drone/pr Build is passing Details
2021-06-23 15:02:37 -07:00
Dan Ballard 225301bda6 drone ./fetch
continuous-integration/drone/push Build is passing Details
2021-06-23 13:55:44 -07:00
Sarah Jamie Lewis 47394f5183 Merge pull request 'update tor versions and fetching of them' (#212) from torUp into trunk
continuous-integration/drone/push Build was killed Details
Reviewed-on: #212
2021-06-23 13:52:10 -07:00
Sarah Jamie Lewis e266d6328a Merge branch 'trunk' into beta_fixes
continuous-integration/drone/pr Build is passing Details
2021-06-22 17:49:18 -07:00
Sarah Jamie Lewis 94014ede01 bump version
continuous-integration/drone/pr Build is passing Details
2021-06-22 17:49:05 -07:00
Sarah Jamie Lewis 066c1965e6 Fix Reconnect Woes
continuous-integration/drone/pr Build is passing Details
2021-06-22 17:05:38 -07:00
Sarah Jamie Lewis ee2fd2ae98 Fix DeleteProfile in Android
continuous-integration/drone/pr Build is passing Details
2021-06-22 12:44:36 -07:00
Dan Ballard c5154a91d1 contacts that are blocked get blocked colors; rework opaque theming a little, start using for font size
continuous-integration/drone/pr Build is failing Details
2021-06-18 20:31:25 -07:00
50 changed files with 742 additions and 575 deletions

View File

@ -1,184 +1,3 @@
---
kind: pipeline
type: docker
name: linux-android-test
clone:
disable: true
steps:
- name: clone
image: cirrusci/flutter:dev
environment:
buildbot_key_b64:
from_secret: buildbot_key_b64
commands:
- mkdir ~/.ssh
- echo $buildbot_key_b64 > ~/.ssh/id_rsa.b64
- base64 -d ~/.ssh/id_rsa.b64 > ~/.ssh/id_rsa
- chmod 400 ~/.ssh/id_rsa
# force by pass of ssh host key check, less secure
- ssh-keyscan -H git.openprivacy.ca >> ~/.ssh/known_hosts
- git clone gogs@git.openprivacy.ca:flutter/flutter_app.git .
- git checkout $DRONE_COMMIT
- name: fetch
image: cirrusci/flutter:dev
volumes:
- name: deps
path: /root/.pub-cache
commands:
- fetch-tor.sh
- echo `git describe --tags` > VERSION
- echo `date +%G-%m-%d-%H-%M` > BUILDDATE
- flutter pub get
- mkdir deploy
- ./fetch-libcwtch-go.sh
#- name: quality
# image: golang
# volumes:
# - name: deps
# path: /go
# commands:
# - go list ./... | xargs go vet
# - go list ./... | xargs golint
# #Todo: fix all the lint errors and add `-set_exit_status` above to enforce linting
- name: build-linux
image: openpriv/flutter-desktop:linux-dev
volumes:
- name: deps
path: /root/.pub-cache
commands:
- flutter build linux --dart-define BUILD_VER=`cat VERSION` --dart-define BUILD_DATE=`cat BUILDDATE`
- mkdir deploy/linux
- cp -r build/linux/x64/release/bundle/* deploy/linux
- cp linux/cwtch.*.desktop deploy/linux
- cp linux/install-*.sh deploy/linux
- cp linux/cwtch.png deploy/linux
- cp linux/libCwtch.so deploy/linux/lib/
# 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 linux/tor deploy/linux
- cd deploy
- mv linux cwtch
- tar -czf cwtch-`cat ../VERSION`.tar.gz cwtch
- rm -r cwtch
- name: test-build-android
image: cirrusci/flutter:dev
when:
event: pull_request
volumes:
- name: deps
path: /root/.pub-cache
commands:
- flutter build apk --debug
- name: build-android
image: cirrusci/flutter:dev
when:
event: push
environment:
upload_jks_file_b64:
from_secret: upload_jks_file_b64
upload_jks_pass:
from_secret: upload_jks_pass
volumes:
- name: deps
path: /root/.pub-cache
commands:
- echo $upload_jks_file_b64 > upload-keystore.jks.b64
- base64 -i --decode upload-keystore.jks.b64 > android/app/upload-keystore.jks
- sed -i "s/%jks-password%/$upload_jks_pass/g" android/key.properties
- flutter build appbundle --dart-define BUILD_VER=`cat VERSION` --dart-define BUILD_DATE=`cat BUILDDATE`
# cant do debug for final release, this is just a stop gap
- flutter build apk --dart-define BUILD_VER=`cat VERSION` --dart-define BUILD_DATE=`cat BUILDDATE`
# or build apk --split-per-abi ?
- cp build/app/outputs/bundle/release/app-release.aab deploy/
- cp build/app/outputs/apk/release/app-release.apk deploy/
#- cp build/app/outputs/flutter-apk/app-debug.apk deploy/android
- name: widget-tests
image: cirrusci/flutter:dev
volumes:
- name: deps
path: /root/.pub-cache
commands:
# - flutter config --enable-linux-desktop
- flutter test --coverage
- genhtml coverage/lcov.info -o coverage/html
# Todo: gonna need more work on container
# https://flutter.dev/desktop
# requirements: Visual Studio 2019 (not to be confused with Visual Studio Code) with the “Desktop development with C++” workload installed, including all of its default components
#- name: build-windows
# image: cirrusci/flutter:dev
#- volumes:
# - name: deps
# path: /root/.pub-cache
# commands:
# - flutter config --enable-windows-desktop
# - flutter build windows
- name: deploy-buildfiles
image: kroniak/ssh-client
environment:
BUILDFILES_KEY:
from_secret: buildfiles_key
secrets: [gogs_account_token]
when:
event: push
status: [ success ]
commands:
- echo $BUILDFILES_KEY > ~/id_rsab64
- base64 -d ~/id_rsab64 > ~/id_rsa
- chmod 400 ~/id_rsa
- export DIR=flwtch-`cat VERSION`-`cat BUILDDATE`
- mv deploy $DIR
- cp -r coverage/html $DIR/coverage-tests
- cp -r test/failures $DIR/test-failures || true
- cd $DIR
- find . -type f -exec sha256sum {} \; > ./../sha256s.txt
- mv ./../sha256s.txt .
- cd ..
# TODO: do deployment once files actaully compile
- scp -r -o StrictHostKeyChecking=no -i ~/id_rsa $DIR buildfiles@openprivacy.ca:/home/buildfiles/buildfiles/
- name: notify-email
image: drillster/drone-email
settings:
host: build.openprivacy.ca
port: 25
skip_verify: true
from: drone@openprivacy.ca
when:
status: [ failure ]
- name: notify-gogs
image: openpriv/drone-gogs
when:
event: pull_request
status: [ success, changed, failure ]
environment:
GOGS_ACCOUNT_TOKEN:
from_secret: gogs_account_token
settings:
gogs_url: https://git.openprivacy.ca
volumes:
- name: deps
temp: {}
trigger:
repo: flutter/flutter_app
branch: trunk
event:
- push
- pull_request
---
kind: pipeline
type: docker
name: windows
@ -218,46 +37,63 @@ steps:
- git describe --tags > VERSION
- powershell -command "Get-Date -Format 'yyyy-MM-dd-HH-mm'" > BUILDDATE
- .\fetch-libcwtch-go.ps1
-
- name: build-windows
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
commands:
- flutter pub get
# flwtch-`cat VERSION`-`cat BUILDDATE`
- $Env:buildname = 'flwtch-win-'
- $Env:version += type .\VERSION
- $Env:buildname += $Env:version
- $Env:buildname += '-'
- $Env:builddate += type .\BUILDDATE
- $Env:buildname += $Env:builddate
- $Env:builddir += $Env:buildname
- $Env:zip = 'cwtch-'
- $Env:zip += $Env:version
- $Env:zip += '.zip'
- $Env:sha = $Env:zip
- $Env:sha += '.sha512'
- $Env:releasedir = "build\\windows\\runner\\Release\\"
- flutter build windows --dart-define BUILD_VER=$Env:version --dart-define BUILD_DATE=$Env:builddate
- mkdir deploy
- move build\\windows\\runner\\Release $Env:builddir
- copy windows\libCwtch.dll $Env:builddir
- copy windows\libCwtch.dll $Env:releasedir
# 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
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\vcruntime140.dll $Env:releasedir
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\vcruntime140_1.dll $Env:releasedir
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\msvcp140.dll $Env:releasedir
- powershell -command "Expand-Archive -Path tor.zip -DestinationPath $Env:releasedir\Tor"
- name: package-windows
image: openpriv/nsis:latest
environment:
pfx:
from_secret: pfx
pfx_pass:
from_secret: pfx_pass
commands:
- $Env:version += type .\VERSION
- $Env:builddate += type .\BUILDDATE
- $Env:releasedir = "build\\windows\\runner\\Release\\"
- $Env:zip = 'cwtch-' + $Env:version + '.zip'
- $Env:zipsha = $Env:zip + '.sha512'
- $Env:msix = 'cwtch-install-' + $Env:version + '.msix'
- $Env:msixsha = $Env:msix + '.sha512'
- $Env:buildname = 'flwtch-win-' + $Env:version + '-' + $Env:builddate
- $Env:builddir = $Env:buildname
- echo $Env:pfx > codesign.pfx.b64
- certutil -decode codesign.pfx.b64 codesign.pfx
- signtool sign /v /fd sha256 /a /f codesign.pfx /p $Env:pfx_pass /tr http://timestamp.digicert.com $Env:releasedir\cwtch.exe
- copy windows\runner\resources\knot_128.ico $Env:releasedir\cwtch.ico
- makensis windows\nsis\cwtch-installer.nsi
- move windows\nsis\cwtch-installer.exe cwtch-installer.exe
- signtool sign /v /fd sha256 /a /f codesign.pfx /p $Env:pfx_pass /tr http://timestamp.digicert.com cwtch-installer.exe
- powershell -command "(Get-FileHash cwtch-installer.exe -Algorithm sha512).Hash" > cwtch-installer.sha512
- mkdir deploy
- mkdir deploy\$Env:builddir
- move $Env:zip deploy\$Env:builddir
- move $Env:sha deploy\$Env:builddir
- move $Env:releasedir $Env:builddir
- powershell -command "Compress-Archive -Path $Env:builddir -DestinationPath cwtch.zip"
- powershell -command "(Get-FileHash cwtch.zip -Algorithm sha512).Hash" > $Env:zipsha
- move cwtch-installer.exe deploy\$Env:builddir\cwtch-installer.exe
- move cwtch.zip deploy\$Env:builddir\$Env:zip
- move *.sha512 deploy\$Env:builddir
- name: deploy-windows
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
when:
event: push
status: [ success ]
event: push
status: [ success ]
environment:
BUILDFILES_KEY:
from_secret: buildfiles_key

View File

@ -1 +1 @@
v0.0.2-99-gc864bcc-2021-06-22-00-52
v0.0.2-108-g3964348-2021-06-24-17-42

9
LICENSE Normal file
View File

@ -0,0 +1,9 @@
MIT License
Copyright (c) 2021 Open Privacy Research Society
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,4 +1,4 @@
# flwtch
# Cwtch UI
A Flutter based Cwtch UI

View File

@ -181,8 +181,8 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
}
"DeleteProfile" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val groupHandle = (a.get("pass") as? String) ?: ""
Cwtch.deleteProfile(profile, groupHandle)
val pass = (a.get("pass") as? String) ?: ""
Cwtch.deleteProfile(profile, pass)
}
"LeaveConversation" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""

View File

@ -136,12 +136,20 @@ class MainActivity: FlutterActivity() {
super.onResume()
Log.i("MainActivity.kt", "onResume")
if (myReceiver == null) {
Log.i("MainActivity.kt", "onResume registering localbroadcastreceiver")
Log.i("MainActivity.kt", "onResume registering local broadcast receiver / event bus forwarder")
val mc = MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CWTCH_EVENTBUS)
val filter = IntentFilter("im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS")
myReceiver = MyBroadcastReceiver(mc)
LocalBroadcastManager.getInstance(applicationContext).registerReceiver(myReceiver!!, filter)
}
// ReconnectCwtchForeground which will resync counters and settings...
// We need to do this here because after a "pause" flutter is still running
// but we might have lost sync with the background process...
Log.i("MainActivity.kt", "Call ReconnectCwtchForeground")
val data: Data = Data.Builder().putString(FlwtchWorker.KEY_METHOD, "ReconnectCwtchForeground").putString(FlwtchWorker.KEY_ARGS, "{}").build()
val workRequest = OneTimeWorkRequestBuilder<FlwtchWorker>().setInputData(data).build()
WorkManager.getInstance(applicationContext).enqueue(workRequest)
}
override fun onStop() {
@ -155,7 +163,7 @@ class MainActivity: FlutterActivity() {
override fun onDestroy() {
super.onDestroy()
Log.i("MainActivity.kt", "onDestroy")
Log.i("MainActivity.kt", "onDestroy - cancelling all WORKER_TAG and pruning old work")
WorkManager.getInstance(this).cancelAllWorkByTag(WORKER_TAG)
WorkManager.getInstance(this).pruneWork()
}
@ -180,6 +188,8 @@ class MainActivity: FlutterActivity() {
val Data = this.optString("Data")
}
// MainActivity.MyBroadcastReceiver receives events from the Cwtch service via im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS Android local broadcast intents
// then it forwards them to the flutter ui engine using the CWTCH_EVENTBUS methodchannel
class MyBroadcastReceiver(mc: MethodChannel) : BroadcastReceiver() {
val eventBus: MethodChannel = mc
@ -190,5 +200,4 @@ class MainActivity: FlutterActivity() {
eventBus.invokeMethod(evtType, evtData)
}
}
}

View File

@ -1,6 +1,6 @@
Invoke-WebRequest -Uri https://www.torproject.org/dist/torbrowser/10.0.16/tor-win32-0.4.5.7.zip -OutFile tor.zip
Invoke-WebRequest -Uri https://dist.torproject.org/torbrowser/10.0.18/tor-win64-0.4.5.9.zip -OutFile tor.zip
if ((Get-FileHash tor.zip -Algorithm sha512).Hash -ne '2b7d683f036d0fec149f1d2bdfcf5b7ef4c337005a2b685c056b00047fdb2b57d4c25b8559ad7ef5c7a030b273934be82a9f83ef6e391f5d7d13d8d6c83e8048' ) { Write-Error 'tor.zip sha512sum mismatch' }
if ((Get-FileHash tor.zip -Algorithm sha512).Hash -ne '72764eb07ad8ab511603aba0734951ca003989f5f4686af91ba220217b4a8a4bcc5f571b59f52c847932f6efedf847b111621983050fcddbb8099d43ca66fb07' ) { Write-Error 'tor.zip sha512sum mismatch' }
Expand-Archive -Path tor.zip -DestinationPath Tor

View File

@ -37,8 +37,8 @@ class CwtchNotifier {
appState.SetAppError(data["Error"]);
break;
case "NewPeer":
profileCN.add(ProfileInfoState(
onion: data["Identity"], nickname: data["name"], imagePath: data["picture"], contactsJson: data["ContactsJson"], serversJson: data["ServerList"], online: data["Online"] == "true"));
// if tag != v1-defaultPassword then it is either encrypted OR it is an unencrypted account created during pre-beta...
profileCN.add(data["Identity"], data["name"], data["picture"], data["ContactsJson"], data["ServerList"], data["Online"] == "true", data["tag"] != "v1-defaultPassword");
break;
case "PeerCreated":
profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(
@ -98,7 +98,9 @@ class CwtchNotifier {
break;
case "NewMessageFromPeer":
notificationManager.notify("New Message From Peer!");
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.unreadMessages++;
if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["RemotePeer"]) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.unreadMessages++;
}
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.totalMessages++;
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["RemotePeer"], DateTime.now());
break;
@ -123,7 +125,9 @@ class CwtchNotifier {
case "NewMessageFromGroup":
if (data["ProfileOnion"] != data["RemotePeer"]) {
//not from me
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.unreadMessages++;
if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["GroupID"]) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.unreadMessages++;
}
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.totalMessages++;
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now());
} else {

View File

@ -83,7 +83,7 @@ class CwtchGomobile implements Cwtch {
// ignore: non_constant_identifier_names
void DeleteProfile(String onion, String pass) {
cwtchPlatform.invokeMethod("DeleteProfile", {"onion": onion, "pass": pass});
cwtchPlatform.invokeMethod("DeleteProfile", {"ProfileOnion": onion, "pass": pass});
}
// ignore: non_constant_identifier_names

View File

@ -1,6 +1,10 @@
{
"@@locale": "de",
"@@last_modified": "2021-06-16T23:15:48+02:00",
"@@last_modified": "2021-06-24T23:32:06+02:00",
"tooltipHidePassword": "Hide Password",
"tooltipShowPassword": "Show Password",
"serverNotSynced": "Syncing New Messages (This can take some time)...",
"groupInviteSettingsWarning": "You have been invited to join a group! Please enable the Group Chat Experiment in Settings to view this Invitation.",
"shutdownCwtchAction": "Shutdown Cwtch",
"shutdownCwtchDialog": "Are you sure you want to shutdown Cwtch? This will close all connections, and exit the application.",
"shutdownCwtchDialogTitle": "Shutdown Cwtch?",
@ -154,7 +158,6 @@
"inviteToGroupLabel": "In die Gruppe einladen",
"groupNameLabel": "Gruppenname",
"viewServerInfo": "Server Info",
"serverNotSynced": "Out of Sync",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server getrennt",
"serverConnectivityConnected": "Server verbunden",

View File

@ -1,6 +1,10 @@
{
"@@locale": "en",
"@@last_modified": "2021-06-16T23:15:48+02:00",
"@@last_modified": "2021-06-24T23:32:06+02:00",
"tooltipHidePassword": "Hide Password",
"tooltipShowPassword": "Show Password",
"serverNotSynced": "Syncing New Messages (This can take some time)...",
"groupInviteSettingsWarning": "You have been invited to join a group! Please enable the Group Chat Experiment in Settings to view this Invitation.",
"shutdownCwtchAction": "Shutdown Cwtch",
"shutdownCwtchDialog": "Are you sure you want to shutdown Cwtch? This will close all connections, and exit the application.",
"shutdownCwtchDialogTitle": "Shutdown Cwtch?",
@ -154,7 +158,6 @@
"inviteToGroupLabel": "Invite to group",
"groupNameLabel": "Group Name",
"viewServerInfo": "Server Info",
"serverNotSynced": "Out of Sync",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server Disconnected",
"serverConnectivityConnected": "Server Connected",

View File

@ -1,6 +1,10 @@
{
"@@locale": "es",
"@@last_modified": "2021-06-16T23:15:48+02:00",
"@@last_modified": "2021-06-24T23:32:06+02:00",
"tooltipHidePassword": "Hide Password",
"tooltipShowPassword": "Show Password",
"serverNotSynced": "Fuera de sincronización con el servidor",
"groupInviteSettingsWarning": "You have been invited to join a group! Please enable the Group Chat Experiment in Settings to view this Invitation.",
"shutdownCwtchAction": "Shutdown Cwtch",
"shutdownCwtchDialog": "Are you sure you want to shutdown Cwtch? This will close all connections, and exit the application.",
"shutdownCwtchDialogTitle": "Shutdown Cwtch?",
@ -154,7 +158,6 @@
"inviteToGroupLabel": "Invitar al grupo",
"groupNameLabel": "Nombre del grupo",
"viewServerInfo": "Información del servidor",
"serverNotSynced": "Fuera de sincronización con el servidor",
"serverSynced": "Sincronizado",
"serverConnectivityDisconnected": "Servidor desconectado",
"serverConnectivityConnected": "Servidor conectado",

View File

@ -1,6 +1,10 @@
{
"@@locale": "fr",
"@@last_modified": "2021-06-16T23:15:48+02:00",
"@@last_modified": "2021-06-24T23:32:06+02:00",
"tooltipHidePassword": "Hide Password",
"tooltipShowPassword": "Show Password",
"serverNotSynced": "Syncing New Messages (This can take some time)...",
"groupInviteSettingsWarning": "You have been invited to join a group! Please enable the Group Chat Experiment in Settings to view this Invitation.",
"shutdownCwtchAction": "Shutdown Cwtch",
"shutdownCwtchDialog": "Are you sure you want to shutdown Cwtch? This will close all connections, and exit the application.",
"shutdownCwtchDialogTitle": "Shutdown Cwtch?",
@ -154,7 +158,6 @@
"inviteToGroupLabel": "Inviter quelqu'un",
"groupNameLabel": "Nom du groupe",
"viewServerInfo": "Server Info",
"serverNotSynced": "Out of Sync",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server Disconnected",
"serverConnectivityConnected": "Server Connected",

View File

@ -1,6 +1,10 @@
{
"@@locale": "it",
"@@last_modified": "2021-06-16T23:15:48+02:00",
"@@last_modified": "2021-06-24T23:32:06+02:00",
"tooltipHidePassword": "Hide Password",
"tooltipShowPassword": "Show Password",
"serverNotSynced": "Non sincronizzato",
"groupInviteSettingsWarning": "You have been invited to join a group! Please enable the Group Chat Experiment in Settings to view this Invitation.",
"shutdownCwtchAction": "Shutdown Cwtch",
"shutdownCwtchDialog": "Are you sure you want to shutdown Cwtch? This will close all connections, and exit the application.",
"shutdownCwtchDialogTitle": "Shutdown Cwtch?",
@ -154,7 +158,6 @@
"inviteToGroupLabel": "Invitare nel gruppo",
"groupNameLabel": "Nome del gruppo",
"viewServerInfo": "Informazioni sul server",
"serverNotSynced": "Non sincronizzato",
"serverSynced": "Sincronizzato",
"serverConnectivityDisconnected": "Server disconnesso",
"serverConnectivityConnected": "Server connesso",

View File

@ -1,6 +1,10 @@
{
"@@locale": "pt",
"@@last_modified": "2021-06-16T23:15:48+02:00",
"@@last_modified": "2021-06-24T23:32:06+02:00",
"tooltipHidePassword": "Hide Password",
"tooltipShowPassword": "Show Password",
"serverNotSynced": "Syncing New Messages (This can take some time)...",
"groupInviteSettingsWarning": "You have been invited to join a group! Please enable the Group Chat Experiment in Settings to view this Invitation.",
"shutdownCwtchAction": "Shutdown Cwtch",
"shutdownCwtchDialog": "Are you sure you want to shutdown Cwtch? This will close all connections, and exit the application.",
"shutdownCwtchDialogTitle": "Shutdown Cwtch?",
@ -154,7 +158,6 @@
"inviteToGroupLabel": "Convidar ao grupo",
"groupNameLabel": "Nome do Grupo",
"viewServerInfo": "Server Info",
"serverNotSynced": "Out of Sync",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server Disconnected",
"serverConnectivityConnected": "Server Connected",

View File

@ -114,4 +114,7 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''');
yield LicenseEntryWithLineBreaks(["flaticons"], "Icons made by Freepik (https://www.freepik.com) from Flaticon (www.flaticon.com)");
}

View File

@ -22,7 +22,7 @@ import 'dart:io' show Platform, exit;
import 'opaque.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
var globalSettings = Settings(Locale("en", ''), Opaque.dark);
var globalSettings = Settings(Locale("en", ''), OpaqueDark());
var globalErrorHandler = ErrorHandler();
var globalTorStatus = TorStatus();
var globalAppState = AppState();
@ -45,10 +45,6 @@ class Flwtch extends StatefulWidget {
class FlwtchState extends State<Flwtch> {
final TextStyle biggerFont = const TextStyle(fontSize: 18);
late Cwtch cwtch;
late ProfileInfoState selectedProfile;
String selectedConversation = "";
var columns = [1]; // default or 'single column' mode
//var columns = [1, 1, 2];
late ProfileListState profs;
final MethodChannel notificationClickChannel = MethodChannel('im.cwtch.flwtch/notificationClickHandler');
final MethodChannel shutdownMethodChannel = MethodChannel('im.cwtch.flwtch/shutdown');
@ -108,7 +104,7 @@ class FlwtchState extends State<Flwtch> {
supportedLocales: AppLocalizations.supportedLocales,
title: 'Cwtch',
theme: mkThemeData(settings),
home: appState.cwtchInit == true ? (columns.length == 3 ? TripleColumnView() : ShiftRightFixer(child: ProfileMgrView())) : SplashView(),
home: appState.cwtchInit == true ? ShiftRightFixer(child: ProfileMgrView()) : SplashView(),
),
);
},
@ -128,24 +124,38 @@ class FlwtchState extends State<Flwtch> {
});
}
// Invoked via notificationClickChannel by MyBroadcastReceiver in MainActivity.kt
// coder beware: args["RemotePeer"] is actually a handle, and could be eg a groupID
Future<void> _externalNotificationClicked(MethodCall call) async {
var args = jsonDecode(call.arguments);
var profile = profs.getProfile(args["ProfileOnion"])!;
var contact = profile.contactList.getContact(args["RemotePeer"])!;
contact.unreadMessages = 0;
navKey.currentState?.push(
MaterialPageRoute<void>(
builder: (BuildContext builderContext) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: profile),
ChangeNotifierProvider.value(value: contact),
],
builder: (context, child) => MessageView(),
);
},
),
);
// single pane mode pushes; double pane mode reads AppState.selectedProfile/Conversation
var isLandscape = Provider.of<AppState>(navKey.currentContext!, listen: false).isLandscape(navKey.currentContext!);
if (Provider.of<Settings>(navKey.currentContext!, listen: false).uiColumns(isLandscape).length == 1) {
if (navKey.currentContext?.findAncestorWidgetOfExactType<MessageView>() != null) {
print("messageview already open; popping before pushing replacement");
navKey.currentState?.pop();
}
navKey.currentState?.push(
MaterialPageRoute<void>(
builder: (BuildContext builderContext) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: profile),
ChangeNotifierProvider.value(value: contact),
],
builder: (context, child) => MessageView(),
);
},
),
);
} else { //dual pane
Provider.of<AppState>(navKey.currentContext!, listen: false).selectedProfile = args["ProfileOnion"];
Provider.of<AppState>(navKey.currentContext!, listen: false).selectedConversation = args["RemotePeer"];
}
}
@override

View File

@ -13,9 +13,8 @@ import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:glob/glob.dart';
import 'package:glob/list_local_fs.dart';
var globalSettings = Settings(Locale("en", ''), Opaque.dark);
var globalSettings = Settings(Locale("en", ''), OpaqueDark());
var globalErrorHandler = ErrorHandler();
void main() {

View File

@ -38,13 +38,13 @@ class ProfileListState extends ChangeNotifier {
List<ProfileInfoState> _profiles = [];
int get num => _profiles.length;
void addAll(Iterable<ProfileInfoState> newProfiles) {
_profiles.addAll(newProfiles);
notifyListeners();
}
void add(ProfileInfoState newProfile) {
_profiles.add(newProfile);
void add(String onion, String name, String picture, String contactsJson, String serverJson, bool online, bool encrypted) {
var idx = _profiles.indexWhere((element) => element.onion == onion);
if (idx == -1) {
_profiles.add(ProfileInfoState(onion: onion, nickname: name, imagePath: picture, contactsJson: contactsJson, serversJson: serverJson, online: online, encrypted: encrypted));
} else {
_profiles[idx].updateFrom(onion, name, picture, contactsJson, serverJson, online);
}
notifyListeners();
}
@ -64,6 +64,8 @@ class ProfileListState extends ChangeNotifier {
class AppState extends ChangeNotifier {
bool cwtchInit = false;
String appError = "";
String? _selectedProfile;
String? _selectedConversation;
void SetCwtchInit() {
cwtchInit = true;
@ -74,6 +76,20 @@ class AppState extends ChangeNotifier {
appError = error;
notifyListeners();
}
String? get selectedProfile => _selectedProfile;
set selectedProfile(String? newVal) {
this._selectedProfile = newVal;
notifyListeners();
}
String? get selectedConversation => _selectedConversation;
set selectedConversation(String? newVal) {
this._selectedConversation = newVal;
notifyListeners();
}
bool isLandscape(BuildContext c) => MediaQuery.of(c).size.width > MediaQuery.of(c).size.height;
}
class ContactListState extends ChangeNotifier {
@ -161,6 +177,10 @@ class ProfileInfoState extends ChangeNotifier {
int _unreadMessages = 0;
bool _online = false;
// assume profiles are encrypted...this will be set to false
// in the constructor if the profile is encrypted with the defacto password.
bool _encrypted = true;
ProfileInfoState({
required this.onion,
nickname = "",
@ -169,11 +189,13 @@ class ProfileInfoState extends ChangeNotifier {
contactsJson = "",
serversJson = "",
online = false,
encrypted = true,
}) {
this._nickname = nickname;
this._imagePath = imagePath;
this._unreadMessages = unreadMessages;
this._online = online;
this._encrypted = encrypted;
if (contactsJson != null && contactsJson != "" && contactsJson != "null") {
List<dynamic> contacts = jsonDecode(contactsJson);
@ -227,6 +249,9 @@ class ProfileInfoState extends ChangeNotifier {
notifyListeners();
}
// Check encrypted status for profile info screen
bool get isEncrypted => this._encrypted;
String get nickname => this._nickname;
set nickname(String newValue) {
this._nickname = newValue;
@ -261,6 +286,41 @@ class ProfileInfoState extends ChangeNotifier {
super.dispose();
print("profileinfostate.dispose()");
}
void updateFrom(String onion, String name, String picture, String contactsJson, String serverJson, bool online) {
this._nickname = name;
this._imagePath = picture;
this._online = online;
this.replaceServers(serverJson);
if (contactsJson != null && contactsJson != "" && contactsJson != "null") {
List<dynamic> contacts = jsonDecode(contactsJson);
contacts.forEach((contact) {
var profileContact = this._contacts.getContact(contact["onion"]);
if (profileContact != null) {
profileContact.status = contact["status"];
profileContact.totalMessages = contact["numMessages"];
profileContact.lastMessageTime = DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"]));
} else {
this._contacts.add(ContactInfoState(
this.onion,
contact["onion"],
nickname: contact["name"],
status: contact["status"],
imagePath: contact["picture"],
isBlocked: contact["authorization"] == "blocked",
isInvitation: contact["authorization"] == "unknown",
savePeerHistory: contact["saveConversationHistory"],
numMessages: contact["numMessages"],
numUnread: contact["numUnread"],
isGroup: contact["isGroup"],
server: contact["groupServer"],
lastMessageTime: DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"])),
));
}
});
}
}
}
class ContactInfoState extends ChangeNotifier {
@ -377,7 +437,8 @@ class ContactInfoState extends ChangeNotifier {
bool isOnline() {
if (this.isGroup == true) {
return this.status == "Synced";
// We now have an out of sync warning so we will mark these as online...
return this.status == "Authenticated" || this.status == "Synced";
} else {
return this.status == "Authenticated";
}
@ -456,6 +517,8 @@ class MessageState extends ChangeNotifier {
}
set loaded(bool newVal) {
// quickly-arriving messages get discarded before loading sometimes
if (!hasListeners) return;
this._loaded = newVal;
notifyListeners();
}

View File

@ -10,6 +10,11 @@ import 'package:cwtch/settings.dart';
abstract class OpaqueThemeType {
static final Color red = Color(0xFFFF0000);
String identifier() {
return "dummy";
}
Color backgroundMainColor() {
return red;
}
@ -304,9 +309,15 @@ abstract class OpaqueThemeType {
// ... more to come
// Sizes
double contactOnionTextSize() {
return 18;
}
}
class CwtchDark extends OpaqueThemeType {
class OpaqueDark extends OpaqueThemeType {
static final Color darkGreyPurple = Color(0xFF281831);
static final Color deepPurple = Color(0xFF422850);
static final Color mauvePurple = Color(0xFF8E64A5);
@ -319,6 +330,10 @@ class CwtchDark extends OpaqueThemeType {
static final Color softGreen = Color(0xFFA0FFB0);
static final Color softRed = Color(0xFFFFA0B0);
String identifier() {
return "dark";
}
Color backgroundMainColor() {
return darkGreyPurple;
}
@ -612,7 +627,7 @@ class CwtchDark extends OpaqueThemeType {
}
}
class CwtchLight extends OpaqueThemeType {
class OpaqueLight extends OpaqueThemeType {
static final Color whitePurple = Color(0xFFFFFDFF);
static final Color softPurple = Color(0xFFFDF3FC);
static final Color purple = Color(0xFFDFB9DE);
@ -625,6 +640,10 @@ class CwtchLight extends OpaqueThemeType {
static final Color softGreen = Color(0xFFA0FFB0);
static final Color softRed = Color(0xFFFFA0B0);
String identifier() {
return "light";
}
Color backgroundMainColor() {
return whitePurple;
}
@ -918,6 +937,99 @@ class CwtchLight extends OpaqueThemeType {
}
}
ThemeData mkThemeData(Settings opaque) {
return ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
primarySwatch: Colors.red,
primaryIconTheme: IconThemeData(
color: opaque.current().mainTextColor(),
),
primaryColor: opaque.current().backgroundMainColor(),
canvasColor: opaque.current().backgroundPaneColor(),
backgroundColor: opaque.current().backgroundMainColor(),
highlightColor: opaque.current().hilightElementTextColor(),
iconTheme: IconThemeData(
color: opaque.current().mainTextColor(),
),
cardColor: opaque.current().backgroundMainColor(),
appBarTheme: AppBarTheme(
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()),
foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor()),
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor()),
padding: MaterialStateProperty.all(EdgeInsets.all(20))),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(opaque.current().defaultButtonColor()),
foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor()),
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor()),
padding: MaterialStateProperty.all(EdgeInsets.all(20)),
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
)),
),
),
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(),
titleTextStyle: TextStyle(color: opaque.current().mainTextColor()),
contentTextStyle: TextStyle(color: opaque.current().mainTextColor())),
textTheme: TextTheme(
headline1: TextStyle(color: opaque.current().mainTextColor()),
headline2: TextStyle(color: opaque.current().mainTextColor()),
headline3: TextStyle(color: opaque.current().mainTextColor()),
headline4: TextStyle(color: opaque.current().mainTextColor()),
headline5: TextStyle(color: opaque.current().mainTextColor()),
headline6: TextStyle(color: opaque.current().mainTextColor()),
bodyText1: TextStyle(color: opaque.current().mainTextColor()),
bodyText2: TextStyle(color: opaque.current().mainTextColor()),
subtitle1: TextStyle(color: opaque.current().mainTextColor()),
subtitle2: TextStyle(color: opaque.current().mainTextColor()),
caption: TextStyle(color: opaque.current().mainTextColor()),
button: TextStyle(color: opaque.current().mainTextColor()),
overline: TextStyle(color: opaque.current().mainTextColor())),
switchTheme: SwitchThemeData(
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor()),
thumbColor: MaterialStateProperty.all(opaque.current().mainTextColor()),
trackColor: MaterialStateProperty.all(opaque.current().dropShadowColor()),
),
floatingActionButtonTheme: FloatingActionButtonThemeData(backgroundColor: opaque.current().defaultButtonColor(), hoverColor: opaque.current().defaultButtonActiveColor()),
textSelectionTheme: TextSelectionThemeData(
cursorColor: opaque.current().defaultButtonActiveColor(), selectionColor: opaque.current().defaultButtonActiveColor(), selectionHandleColor: opaque.current().defaultButtonActiveColor()),
);
}
/*
OpaqueThemeType _current = CwtchDark();
void setDark() {
_current = CwtchDark();
}
void setLight() {
_current = CwtchLight();
}
OpaqueThemeType current() {
if (_current == null) {
setDark();
}
return _current;
}
class Opaque extends OpaqueThemeType {
Color backgroundMainColor() {
return current().backgroundMainColor();
@ -1226,22 +1338,9 @@ class Opaque extends OpaqueThemeType {
}
static late OpaqueThemeType _current;
static final OpaqueThemeType dark = CwtchDark();
static final OpaqueThemeType light = CwtchLight();
static void setDark() {
_current = dark;
}
//static final OpaqueThemeType dark = CwtchDark();
//static final OpaqueThemeType light = CwtchLight();
static void setLight() {
_current = light;
}
static OpaqueThemeType current() {
if (_current == null) {
setDark();
}
return _current;
}
int scale = 2;
static final String gcdOS = "linux";
@ -1341,76 +1440,4 @@ class Opaque extends OpaqueThemeType {
}
}
ThemeData mkThemeData(Settings opaque) {
return ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
primarySwatch: Colors.red,
primaryIconTheme: IconThemeData(
color: opaque.current().mainTextColor(),
),
primaryColor: opaque.current().backgroundMainColor(),
canvasColor: opaque.current().backgroundPaneColor(),
backgroundColor: opaque.current().backgroundMainColor(),
highlightColor: opaque.current().hilightElementTextColor(),
iconTheme: IconThemeData(
color: opaque.current().mainTextColor(),
),
cardColor: opaque.current().backgroundMainColor(),
appBarTheme: AppBarTheme(
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()),
foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor()),
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor()),
padding: MaterialStateProperty.all(EdgeInsets.all(20))),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(opaque.current().defaultButtonColor()),
foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor()),
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor()),
padding: MaterialStateProperty.all(EdgeInsets.all(20)),
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
)),
),
),
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(),
titleTextStyle: TextStyle(color: opaque.current().mainTextColor()),
contentTextStyle: TextStyle(color: opaque.current().mainTextColor())),
textTheme: TextTheme(
headline1: TextStyle(color: opaque.current().mainTextColor()),
headline2: TextStyle(color: opaque.current().mainTextColor()),
headline3: TextStyle(color: opaque.current().mainTextColor()),
headline4: TextStyle(color: opaque.current().mainTextColor()),
headline5: TextStyle(color: opaque.current().mainTextColor()),
headline6: TextStyle(color: opaque.current().mainTextColor()),
bodyText1: TextStyle(color: opaque.current().mainTextColor()),
bodyText2: TextStyle(color: opaque.current().mainTextColor()),
subtitle1: TextStyle(color: opaque.current().mainTextColor()),
subtitle2: TextStyle(color: opaque.current().mainTextColor()),
caption: TextStyle(color: opaque.current().mainTextColor()),
button: TextStyle(color: opaque.current().mainTextColor()),
overline: TextStyle(color: opaque.current().mainTextColor())),
switchTheme: SwitchThemeData(
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor()),
thumbColor: MaterialStateProperty.all(opaque.current().mainTextColor()),
trackColor: MaterialStateProperty.all(opaque.current().dropShadowColor()),
),
floatingActionButtonTheme: FloatingActionButtonThemeData(backgroundColor: opaque.current().defaultButtonColor(), hoverColor: opaque.current().defaultButtonActiveColor()),
textSelectionTheme: TextSelectionThemeData(
cursorColor: opaque.current().defaultButtonActiveColor(), selectionColor: opaque.current().defaultButtonActiveColor(), selectionHandleColor: opaque.current().defaultButtonActiveColor()),
);
}
*/

View File

@ -9,6 +9,13 @@ import 'opaque.dart';
const TapirGroupsExperiment = "tapir-groups-experiment";
enum DualpaneMode {
Single,
Dual1to2,
Dual1to4,
CopyPortrait,
}
/// Settings govern the *Globally* relevant settings like Locale, Theme and Experiments.
/// We also provide access to the version information here as it is also accessed from the
/// Settings Pane.
@ -19,18 +26,20 @@ class Settings extends ChangeNotifier {
// explicitly set experiments to false until told otherwise...
bool experimentsEnabled = false;
HashMap<String, bool> experiments = HashMap.identity();
DualpaneMode _uiColumnModePortrait = DualpaneMode.Single;
DualpaneMode _uiColumnModeLandscape = DualpaneMode.CopyPortrait;
late bool blockUnknownConnections;
bool blockUnknownConnections = false;
/// Set the dark theme.
void setDark() {
theme = Opaque.dark;
theme = OpaqueDark();
notifyListeners();
}
/// Set the Light theme.
void setLight() {
theme = Opaque.light;
theme = OpaqueLight();
notifyListeners();
}
@ -73,6 +82,10 @@ class Settings extends ChangeNotifier {
// Set the internal experiments map. Casting from the Map<dynamic, dynamic> that we get from JSON
experiments = new HashMap<String, bool>.from(settings["Experiments"]);
// single pane vs dual pane preferences
_uiColumnModePortrait = uiColumnModeFromString(settings["UIColumnModePortrait"]);
_uiColumnModeLandscape = uiColumnModeFromString(settings["UIColumnModeLandscape"]);
// Push the experimental settings to Consumers of Settings
notifyListeners();
}
@ -134,16 +147,62 @@ class Settings extends ChangeNotifier {
notifyListeners();
}
DualpaneMode get uiColumnModePortrait => _uiColumnModePortrait;
set uiColumnModePortrait(DualpaneMode newval) {
this._uiColumnModePortrait = newval;
notifyListeners();
}
DualpaneMode get uiColumnModeLandscape => _uiColumnModeLandscape;
set uiColumnModeLandscape(DualpaneMode newval) {
this._uiColumnModeLandscape = newval;
notifyListeners();
}
List<int> uiColumns(bool isLandscape) {
var m = (!isLandscape || uiColumnModeLandscape == DualpaneMode.CopyPortrait) ? uiColumnModePortrait : uiColumnModeLandscape;
switch(m) {
case DualpaneMode.Single: return [1];
case DualpaneMode.Dual1to2: return [1, 2];
case DualpaneMode.Dual1to4: return [1, 4];
}
print("impossible column configuration: portrait/$uiColumnModePortrait landscape/$uiColumnModeLandscape");
return [1];
}
static List<DualpaneMode> uiColumnModeOptions(bool isLandscape) {
if (isLandscape) return [DualpaneMode.CopyPortrait, DualpaneMode.Single, DualpaneMode.Dual1to2, DualpaneMode.Dual1to4,];
else return [DualpaneMode.Single, DualpaneMode.Dual1to2, DualpaneMode.Dual1to4];
}
static DualpaneMode uiColumnModeFromString(String m) {
switch(m) {
case "DualpaneMode.Single": return DualpaneMode.Single;
case "DualpaneMode.Dual1to2": return DualpaneMode.Dual1to2;
case "DualpaneMode.Dual1to4": return DualpaneMode.Dual1to4;
case "DualpaneMode.CopyPortrait": return DualpaneMode.CopyPortrait;
}
print("Error: ui requested translation of column mode [$m] which doesn't exist");
return DualpaneMode.Single;
}
static String uiColumnModeToString(DualpaneMode m) {
// todo: translate
switch(m) {
case DualpaneMode.Single: return "Single";
case DualpaneMode.Dual1to2: return "Double (1:2)";
case DualpaneMode.Dual1to4: return "Double (1:4)";
case DualpaneMode.CopyPortrait: return "Same as portrait mode setting";
}
}
/// Construct a default settings object.
Settings(this.locale, this.theme);
/// Convert this Settings object to a JSON representation for serialization on the
/// event bus.
dynamic asJson() {
var themeString = "light";
if (theme == Opaque.dark) {
themeString = "dark";
}
var themeString = theme.identifier();
return {
"Locale": this.locale.languageCode,
@ -153,7 +212,9 @@ class Settings extends ChangeNotifier {
"ExperimentsEnabled": this.experimentsEnabled,
"Experiments": experiments,
"StateRootPane": 0,
"FirstTime": false
"FirstTime": false,
"UIColumnModePortrait": uiColumnModePortrait.toString(),
"UIColumnModeLandscape": uiColumnModeLandscape.toString(),
};
}
}

View File

@ -155,7 +155,7 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
visible: usePassword,
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
Visibility(
visible: Provider.of<ProfileInfoState>(context, listen: false).onion.isNotEmpty,
visible: Provider.of<ProfileInfoState>(context, listen: false).onion.isNotEmpty && Provider.of<ProfileInfoState>(context).isEncrypted,
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [
CwtchLabel(label: AppLocalizations.of(context)!.currentPasswordLabel),
SizedBox(
@ -165,7 +165,7 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
controller: ctrlrOldPass,
validator: (value) {
// Password field can be empty when just updating the profile, not on creation
if (Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty && value.isEmpty && usePassword) {
if (Provider.of<ProfileInfoState>(context).isEncrypted && Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty && value.isEmpty && usePassword) {
return AppLocalizations.of(context)!.passwordErrorEmpty;
}
if (Provider.of<ErrorHandler>(context).deleteProfileError == true) {

View File

@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../main.dart';
import '../model.dart';
import '../settings.dart';
import 'contactsview.dart';
import 'messageview.dart';
@ -14,24 +15,25 @@ class DoubleColumnView extends StatefulWidget {
class _DoubleColumnViewState extends State<DoubleColumnView> {
@override
Widget build(BuildContext context) {
var flwtch = Provider.of<FlwtchState>(context);
var flwtch = Provider.of<AppState>(context);
var cols = Provider.of<Settings>(context).uiColumns(true);
return Flex(
direction: Axis.horizontal,
children: <Widget>[
Flexible(
flex: flwtch.columns[0],
flex: cols[0],
child: ContactsView(
key: widget.key,
),
),
Flexible(
flex: flwtch.columns[1],
child: flwtch.selectedConversation == ""
? Center(child: Text(AppLocalizations.of(context)!.addContactFirst))
flex: cols[1],
child: flwtch.selectedConversation == null
? Card(child:Center(child: Text(AppLocalizations.of(context)!.addContactFirst)))
: //dev
MultiProvider(providers: [
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context)),
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context).contactList.getContact(flwtch.selectedConversation)!),
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context).contactList.getContact(flwtch.selectedConversation!)!),
], child: Container(child: MessageView())),
),
],

View File

@ -64,7 +64,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
}).toList())),
SwitchListTile(
title: Text(AppLocalizations.of(context)!.settingTheme, style: TextStyle(color: settings.current().mainTextColor())),
value: settings.current() == Opaque.light,
value: settings.current().identifier() == "light",
onChanged: (bool value) {
if (value) {
settings.setLight();
@ -80,24 +80,33 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
secondary: Icon(CwtchIcons.change_theme, color: settings.current().mainTextColor()),
),
ListTile(
title: Text(/*AppLocalizations.of(context)!.settingLanguage*/ "UI Columns", style: TextStyle(color: settings.current().mainTextColor())),
title: Text(/*AppLocalizations.of(context)!.settingLanguage*/ "UI Columns in Portrait Mode", style: TextStyle(color: settings.current().mainTextColor())),
leading: Icon(Icons.table_chart, color: settings.current().mainTextColor()),
trailing: DropdownButton(
value: "Single",
value: settings.uiColumnModePortrait.toString(),
onChanged: (String? newValue) {
if (newValue == "Double (1:2)") {
Provider.of<FlwtchState>(context).columns = [1, 2];
} else if (newValue == "Double (1:4)") {
Provider.of<FlwtchState>(context).columns = [1, 4];
} else {
Provider.of<FlwtchState>(context).columns = [1];
}
settings.uiColumnModePortrait = Settings.uiColumnModeFromString(newValue!);
saveSettings(context);
},
// TODO: Only allow in landscape?
items: (Platform.isAndroid ? ["Single"] : ["Single", "Double (1:2)", "Double (1:4)"]).map<DropdownMenuItem<String>>((String value) {
items: Settings.uiColumnModeOptions(false).map<DropdownMenuItem<String>>((DualpaneMode value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
value: value.toString(),
child: Text(Settings.uiColumnModeToString(value)),
);
}).toList())),
ListTile(
title: Text(/*AppLocalizations.of(context)!.settingLanguage*/ "UI Columns in Landscape Mode", style: TextStyle(color: settings.current().mainTextColor())),
leading: Icon(Icons.table_chart, color: settings.current().mainTextColor()),
trailing: DropdownButton(
value: settings.uiColumnModeLandscape.toString(),
onChanged: (String? newValue) {
settings.uiColumnModeLandscape = Settings.uiColumnModeFromString(newValue!);
saveSettings(context);
},
items: Settings.uiColumnModeOptions(true).map<DropdownMenuItem<String>>((DualpaneMode value) {
return DropdownMenuItem<String>(
value: value.toString(),
child: Text(Settings.uiColumnModeToString(value)),
);
}).toList())),
SwitchListTile(
@ -160,13 +169,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
)),
AboutListTile(
icon: Icon(Icons.info, color: settings.current().mainTextColor()),
applicationIcon: Padding(
padding: EdgeInsets.all(20),
child: Image(
image: AssetImage("assets/knott.png"),
width: 128,
height: 128,
)),
applicationIcon: Padding(padding:EdgeInsets.all(5), child: Icon(CwtchIcons.cwtch_knott)),
applicationName: "Cwtch (Flutter UI)",
applicationVersion: AppLocalizations.of(context)!.versionBuilddate.replaceAll("%1", EnvironmentConfig.BUILD_VER).replaceAll("%2", EnvironmentConfig.BUILD_DATE),
applicationLegalese: '\u{a9} 2021 Open Privacy Research Society',

View File

@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:io';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:cwtch/widgets/profileimage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cwtch/views/peersettingsview.dart';
@ -42,11 +43,24 @@ class _MessageViewState extends State<MessageView> {
@override
Widget build(BuildContext context) {
var appState = Provider.of<AppState>(context);
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
appBar: AppBar(
title: Text(Provider.of<ContactInfoState>(context).nickname),
// setting leading to null makes it do the default behaviour; container() hides it
leading: Provider.of<Settings>(context).uiColumns(appState.isLandscape(context)).length > 1 ? Container() : null,
title: Row(children: [
ProfileImage(
imagePath: Provider.of<ContactInfoState>(context).imagePath,
diameter: 42,
border: Provider.of<Settings>(context).current().portraitOnlineBorderColor(),
badgeTextColor: Colors.red,
badgeColor: Colors.red,
),
SizedBox(
width: 10,
),Text(Provider.of<ContactInfoState>(context).nickname)]),
actions: [
//IconButton(icon: Icon(Icons.chat), onPressed: _pushContactSettings),
//IconButton(icon: Icon(Icons.list), onPressed: _pushContactSettings),

View File

@ -77,7 +77,6 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
};
final setPeerAttributeJson = jsonEncode(setPeerAttribute);
Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(profileOnion, setPeerAttributeJson);
// todo translations
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.nickChangeSuccess));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
},

View File

@ -42,41 +42,45 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
// Prevents Android back button from closing the app on the profile manager screen
// (which would shutdown connections and all kinds of other expensive to generate things)
// TODO pop up a dialogue regarding closing the app?
builder: (context, settings, child) => WillPopScope(
onWillPop: () async {
_showShutdown();
return closeApp;
},
child: Scaffold(
backgroundColor: settings.theme.backgroundMainColor(),
appBar: AppBar(
title: Row(children: [
Image(
image: AssetImage("assets/core/knott-white.png"),
filterQuality: FilterQuality.medium,
isAntiAlias: true,
width: 32,
height: 32,
colorBlendMode: BlendMode.dstIn,
color: Provider.of<Settings>(context).theme.backgroundHilightElementColor(),
builder: (context, settings, child) =>
WillPopScope(
onWillPop: () async {
_showShutdown();
return closeApp;
},
child: Scaffold(
backgroundColor: settings.theme.backgroundMainColor(),
appBar: AppBar(
title: Row(children: [
Image(
image: AssetImage("assets/core/knott-white.png"),
filterQuality: FilterQuality.medium,
isAntiAlias: true,
width: 32,
height: 32,
colorBlendMode: BlendMode.dstIn,
color: Provider
.of<Settings>(context)
.theme
.backgroundHilightElementColor(),
),
SizedBox(
width: 10,
),
Expanded(child: Text(AppLocalizations.of(context)!.titleManageProfiles, style: TextStyle(color: settings.current().mainTextColor())))
]),
actions: getActions(),
),
SizedBox(
width: 10,
floatingActionButton: FloatingActionButton(
onPressed: _pushAddEditProfile,
tooltip: AppLocalizations.of(context)!.addNewProfileBtn,
child: Icon(
Icons.add,
semanticLabel: AppLocalizations.of(context)!.addNewProfileBtn,
),
),
Expanded(child: Text(AppLocalizations.of(context)!.titleManageProfiles, style: TextStyle(color: settings.current().mainTextColor())))
]),
actions: getActions(),
),
floatingActionButton: FloatingActionButton(
onPressed: _pushAddEditProfile,
tooltip: AppLocalizations.of(context)!.addNewProfileBtn,
child: Icon(
Icons.add,
semanticLabel: AppLocalizations.of(context)!.addNewProfileBtn,
),
),
body: _buildProfileManager(),
)),
body: _buildProfileManager(),
)),
);
}

View File

@ -13,7 +13,7 @@ class SplashView extends StatelessWidget {
body: Center(
child: Column(mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [
Image(
image: AssetImage("assets/knott.png"),
image: AssetImage("assets/core/knott-white.png"),
filterQuality: FilterQuality.medium,
isAntiAlias: true,
width: 200,

View File

@ -55,7 +55,7 @@ class _TorStatusView extends State<TorStatusView> {
ListTile(
title: Text(AppLocalizations.of(context)!.torVersion),
subtitle: Text(torStatus.version),
),
),
]))));
});
});

View File

@ -3,9 +3,12 @@ import 'package:cwtch/views/profilemgrview.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../main.dart';
import '../model.dart';
import '../settings.dart';
import 'contactsview.dart';
import 'messageview.dart';
// currently unused but maybe one day?
class TripleColumnView extends StatefulWidget {
@override
_TripleColumnViewState createState() => _TripleColumnViewState();
@ -14,19 +17,22 @@ class TripleColumnView extends StatefulWidget {
class _TripleColumnViewState extends State<TripleColumnView> {
@override
Widget build(BuildContext context) {
var flwtch = Provider.of<FlwtchState>(context);
var appState = Provider.of<AppState>(context);
var settings = Provider.of<Settings>(context);
var columns = settings.uiColumns(appState.isLandscape(context));
return Flex(direction: Axis.horizontal, children: <Widget>[
Flexible(
flex: flwtch.columns[0],
flex: columns[0],
child: ProfileMgrView(),
),
Flexible(
flex: flwtch.columns[1],
child: flwtch.selectedProfile == null ? Center(child: Text(AppLocalizations.of(context)!.createProfileToBegin)) : ContactsView(), //dev
flex: columns[1],
child: appState.selectedProfile == null ? Center(child: Text(AppLocalizations.of(context)!.createProfileToBegin)) : ContactsView(), //dev
),
Flexible(
flex: flwtch.columns[2],
child: flwtch.selectedConversation == ""
flex: columns[2],
child: appState.selectedConversation == null
? Center(child: Text(AppLocalizations.of(context)!.addContactFirst))
: //dev
Container(child: MessageView()),

View File

@ -20,6 +20,8 @@ class _ContactRowState extends State<ContactRow> {
var contact = Provider.of<ContactInfoState>(context);
return Card(
clipBehavior: Clip.antiAlias,
color: Provider.of<AppState>(context).selectedConversation == contact.onion ? Provider.of<Settings>(context).theme.backgroundHilightElementColor() : null,
borderOnForeground: false,
child: InkWell(
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Padding(
@ -31,7 +33,7 @@ class _ContactRowState extends State<ContactRow> {
diameter: 64.0,
imagePath: contact.imagePath,
maskOut: !contact.isOnline(),
border: contact.isOnline() ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor() : Provider.of<Settings>(context).theme.portraitOfflineBorderColor()),
border: contact.isOnline() ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor() : contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedBorderColor() : Provider.of<Settings>(context).theme.portraitOfflineBorderColor()),
),
Expanded(
child: Padding(
@ -41,11 +43,14 @@ class _ContactRowState extends State<ContactRow> {
children: [
Text(
contact.nickname, //(contact.isInvitation ? "invite " : "non-invite ") + (contact.isBlocked ? "blokt" : "nonblokt"),//
style: Provider.of<FlwtchState>(context).biggerFont,
style: TextStyle(fontSize: Provider.of<Settings>(context).theme.contactOnionTextSize(),
color: contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedTextColor() : Provider.of<Settings>(context).theme.mainTextColor()), //Provider.of<FlwtchState>(context).biggerFont,
softWrap: true,
overflow: TextOverflow.visible,
),
Text(contact.onion),
Text(contact.onion,
style: TextStyle(color: contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedTextColor() : Provider.of<Settings>(context).theme.mainTextColor())),
],
))),
Padding(
@ -77,17 +82,19 @@ class _ContactRowState extends State<ContactRow> {
]),
onTap: () {
setState(() {
var flwtch = Provider.of<FlwtchState>(context, listen: false);
flwtch.setState(() => flwtch.selectedConversation = contact.onion);
// case 2/3 handled by Double/TripleColumnView respectively
if (flwtch.columns.length == 1) _pushMessageView(contact.onion);
// requery instead of using contactinfostate directly because sometimes listview gets confused about data that resorts
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(contact.onion)!.unreadMessages = 0;
// triggers update in Double/TripleColumnView
Provider.of<AppState>(context, listen: false).selectedConversation = contact.onion;
// if in singlepane mode, push to the stack
var isLandscape = Provider.of<AppState>(context, listen: false).isLandscape(context);
if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(contact.onion);
});
},
));
}
void _pushMessageView(String handle) {
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.unreadMessages = 0;
var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
Navigator.of(context).push(
MaterialPageRoute<void>(

View File

@ -14,7 +14,6 @@ import 'messagebubbledecorations.dart';
// Like MessageBubble but for displaying chat overlay 100/101 invitations
// Offers the user an accept/reject button if they don't have a matching contact already
// todo: Reject buttons currently aren't tracked and will reset when the message is reloaded
class InvitationBubble extends StatefulWidget {
@override
InvitationBubbleState createState() => InvitationBubbleState();
@ -35,6 +34,7 @@ class InvitationBubbleState extends State<InvitationBubble> {
isAccepted = Provider.of<ProfileInfoState>(context).contactList.getContact(Provider.of<MessageState>(context).inviteTarget) != null;
var prettyDate = "";
var borderRadiousEh = 15.0;
var showGroupInvite = Provider.of<Settings>(context).isExperimentEnabled(TapirGroupsExperiment);
rejected = Provider.of<MessageState>(context).flags & 0x01 == 0x01;
var myKey = Provider.of<MessageState>(context).profileOnion + "::" + Provider.of<MessageState>(context).contactHandle + "::" + Provider.of<MessageState>(context).messageIndex.toString();
@ -66,14 +66,18 @@ class InvitationBubbleState extends State<InvitationBubble> {
return MalformedBubble();
}
var wdgMessage = fromMe
var wdgMessage = isGroup && !showGroupInvite ?
Text(AppLocalizations.of(context)!.groupInviteSettingsWarning) :
fromMe
? 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) {
if (isGroup && !showGroupInvite) {
wdgDecorations = Text('\u202F');
} else 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(AppLocalizations.of(context)!.accepted + '\u202F');
@ -109,7 +113,7 @@ class InvitationBubbleState extends State<InvitationBubble> {
child: Padding(
padding: EdgeInsets.all(9.0),
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, child: Padding(padding: EdgeInsets.all(10.0), child: Icon(isGroup && !showGroupInvite ? CwtchIcons.enable_experiments : CwtchIcons.send_invite, size: 32))),
Center(
widthFactor: 1.0,
child: Column(

View File

@ -19,6 +19,8 @@ class _MessageListState extends State<MessageList> {
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;
bool showSyncing = Provider.of<ContactInfoState>(context).isGroup == true && Provider.of<ContactInfoState>(context).status != "Synced";
return RepaintBoundary(
child: Container(
child: Column(children: [
@ -27,7 +29,10 @@ class _MessageListState extends State<MessageList> {
child: Container(
padding: EdgeInsets.all(5.0),
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
child: showOfflineWarning
child: showSyncing ?
Text(AppLocalizations.of(context)!.serverNotSynced,
textAlign: TextAlign.center)
: 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...
@ -50,6 +55,7 @@ class _MessageListState extends State<MessageList> {
alignment: Alignment.center,
image: AssetImage("assets/core/negative_heart_512px.png"),
colorFilter: ColorFilter.mode(Provider.of<Settings>(context).theme.hilightElementTextColor(), BlendMode.srcIn))),
// Don't load messages for syncing server...
child: ListView.builder(
controller: ctrlr1,
itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages,

View File

@ -2,6 +2,7 @@ import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../settings.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
// Provides a styled Password Input Field for use in Form Widgets.
// Callers must provide a text controller, label helper text and a validator.
@ -22,9 +23,9 @@ class _CwtchTextFieldState extends State<CwtchPasswordField> {
@override
Widget build(BuildContext context) {
// todo: translations
var label = "View Password";
var label = AppLocalizations.of(context)!.tooltipShowPassword;
if (!obscureText) {
label = "Hide Password";
label = AppLocalizations.of(context)!.tooltipHidePassword;
}
return Consumer<Settings>(builder: (context, theme, child) {

View File

@ -38,7 +38,7 @@ class _ProfileImageState extends State<ProfileImage> {
filterQuality: FilterQuality.medium,
// We need some theme specific blending here...we might want to consider making this a theme level attribute
colorBlendMode: !widget.maskOut
? Provider.of<Settings>(context).theme == Opaque.dark
? Provider.of<Settings>(context).theme.identifier() == "dark"
? BlendMode.softLight
: BlendMode.darken
: BlendMode.srcOut,

View File

@ -30,84 +30,96 @@ class _ProfileRowState extends State<ProfileRow> {
padding: const EdgeInsets.all(2.0), //border size
child: ProfileImage(
badgeCount: 0,
badgeColor: Provider.of<Settings>(context).theme.portraitProfileBadgeColor(),
badgeTextColor: Provider.of<Settings>(context).theme.portraitProfileBadgeTextColor(),
badgeColor: Provider
.of<Settings>(context)
.theme
.portraitProfileBadgeColor(),
badgeTextColor: Provider
.of<Settings>(context)
.theme
.portraitProfileBadgeTextColor(),
diameter: 64.0,
imagePath: profile.imagePath,
border: profile.isOnline ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor() : Provider.of<Settings>(context).theme.portraitOfflineBorderColor())),
border: profile.isOnline ? Provider
.of<Settings>(context)
.theme
.portraitOnlineBorderColor() : Provider
.of<Settings>(context)
.theme
.portraitOfflineBorderColor())),
Expanded(
child: Column(
children: [
Text(
profile.nickname,
semanticsLabel: profile.nickname,
style: Provider.of<FlwtchState>(context).biggerFont,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
ExcludeSemantics(
child: Text(
profile.onion,
softWrap: true,
overflow: TextOverflow.ellipsis,
))
],
)),
children: [
Text(
profile.nickname,
semanticsLabel: profile.nickname,
style: Provider
.of<FlwtchState>(context)
.biggerFont,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
ExcludeSemantics(
child: Text(
profile.onion,
softWrap: true,
overflow: TextOverflow.ellipsis,
))
],
)),
IconButton(
enableFeedback: true,
tooltip: AppLocalizations.of(context)!.editProfile + " " + profile.nickname,
icon: Icon(Icons.create, color: Provider.of<Settings>(context).current().mainTextColor()),
onPressed: () {
_pushAddEditProfile(onion: profile.onion, displayName: profile.nickname, profileImage: profile.imagePath);
_pushAddEditProfile(onion: profile.onion, displayName: profile.nickname, profileImage: profile.imagePath, encrypted: profile.isEncrypted);
},
)
],
),
onTap: () {
setState(() {
var flwtch = Provider.of<FlwtchState>(context, listen: false);
flwtch.setState(() {
flwtch.selectedProfile = profile;
flwtch.selectedConversation = "";
});
var appState = Provider.of<AppState>(context, listen: false);
appState.selectedProfile = profile.onion;
appState.selectedConversation = null;
switch (flwtch.columns.length) {
case 1:
_pushContactList(profile, false);
break;
case 2:
_pushContactList(profile, true);
break;
} // case 3: handled by TripleColumnView
_pushContactList(profile, appState.isLandscape(context));//orientation == Orientation.landscape);
});
},
));
}
void _pushContactList(ProfileInfoState profile, bool includeDoublePane) {
void _pushContactList(ProfileInfoState profile, bool isLandscape) {
Navigator.of(context).push(
MaterialPageRoute<void>(
settings: RouteSettings(name: "conversations"),
builder: (BuildContext buildcontext) {
return MultiProvider(
providers: [
ChangeNotifierProvider<ProfileInfoState>.value(value: profile),
ChangeNotifierProvider<ContactListState>.value(value: profile.contactList),
],
builder: (context, widget) => includeDoublePane ? DoubleColumnView() : ContactsView(),
);
return OrientationBuilder(
builder: (orientationBuilderContext, orientation) {
return MultiProvider(
providers: [
ChangeNotifierProvider<ProfileInfoState>.value(value: profile),
ChangeNotifierProvider<ContactListState>.value(value: profile.contactList),
],
builder: (innercontext, widget) {
var appState = Provider.of<AppState>(context);
var settings = Provider.of<Settings>(context);
return settings.uiColumns(appState.isLandscape(innercontext)).length > 1 ? DoubleColumnView() : ContactsView();
}
);
});
},
),
);
}
void _pushAddEditProfile({onion: "", displayName: "", profileImage: ""}) {
void _pushAddEditProfile({onion: "", displayName: "", profileImage: "", encrypted: true}) {
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<ProfileInfoState>(
create: (_) => ProfileInfoState(onion: onion, nickname: displayName, imagePath: profileImage),
create: (_) => ProfileInfoState(onion: onion, nickname: displayName, imagePath: profileImage, encrypted: encrypted),
),
],
builder: (context, widget) => AddEditProfileView(),

View File

@ -6,10 +6,6 @@
#include "generated_plugin_registrant.h"
#include <window_size/window_size_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) window_size_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowSizePlugin");
window_size_plugin_register_with_registrar(window_size_registrar);
}

View File

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

View File

@ -1,13 +1,13 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
archive:
ansicolor:
dependency: transitive
description:
name: archive
name: ansicolor
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.2"
version: "2.0.1"
args:
dependency: transitive
description:
@ -42,7 +42,7 @@ packages:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.1"
clock:
dependency: transitive
description:
@ -57,13 +57,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
cupertino_icons:
dependency: "direct main"
description:
@ -111,11 +104,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_driver:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_localizations:
dependency: "direct main"
description: flutter
@ -131,11 +119,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
fuchsia_remote_debug_protocol:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
glob:
dependency: "direct main"
description:
@ -157,13 +140,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
integration_test:
dependency: "direct main"
injector:
dependency: transitive
description:
name: integration_test
name: injector
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2+3"
version: "2.0.0"
intl:
dependency: transitive
description:
@ -192,6 +175,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0"
msix:
dependency: "direct dev"
description:
name: msix
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
nested:
dependency: transitive
description:
@ -213,6 +203,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
package_config:
dependency: transitive
description:
name: package_config
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
package_info_plus:
dependency: "direct main"
description:
@ -372,13 +369,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
sync_http:
dependency: transitive
description:
name: sync_http
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.0"
term_glyph:
dependency: transitive
description:
@ -392,7 +382,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.0"
version: "0.4.1"
typed_data:
dependency: transitive
description:
@ -407,20 +397,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
vm_service:
dependency: transitive
description:
name: vm_service
url: "https://pub.dartlang.org"
source: hosted
version: "6.2.0"
webdriver:
dependency: transitive
description:
name: webdriver
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
win32:
dependency: transitive
description:
@ -428,15 +404,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
window_size:
dependency: "direct main"
description:
path: "plugins/window_size"
ref: e48abe7c3e9ebfe0b81622167c5201d4e783bb81
resolved-ref: e48abe7c3e9ebfe0b81622167c5201d4e783bb81
url: "git://github.com/google/flutter-desktop-embedding.git"
source: git
version: "0.1.0"
xdg_directories:
dependency: transitive
description:
@ -451,6 +418,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.2"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
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+13
version: 1.0.0+15
environment:
sdk: ">=2.12.0 <3.0.0"
@ -37,20 +37,11 @@ dependencies:
desktop_notifications: 0.5.0
glob: any
# todo: flutter_driver causes version conflict. eg https://github.com/flutter/flutter/issues/44829
# testing-related deps
integration_test: ^1.0.0
flutter_test:
sdk: flutter
flutter_driver:
sdk: flutter
window_size:
git:
url: git://github.com/google/flutter-desktop-embedding.git
path: plugins/window_size
ref: e48abe7c3e9ebfe0b81622167c5201d4e783bb81
dev_dependencies:
msix: ^2.1.3
# Uncomment to update lokalise translations (see README for list of deps to comment out bc incompatibilities)
#dev_dependencies:
# flutter_lokalise: any
@ -116,3 +107,19 @@ flutter:
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
msix_config:
display_name: Cwtch
publisher_display_name: Open Privacy Research Society
identity_name: im.cwtch.flwtch
msix_version: 1.0.0.0
certificate_path: codesign.pfx
certificate_password: pfx_pass
publisher: CN=Open Privacy Research Society, O=Open Privacy Research Society, L=Vancouver, S=British Columbia, C=CA
logo_path: cwtch.png
start_menu_icon_path: cwtch.png
tile_icon_path: assets\cwtch_title.png
icons_background_color: transparent
architecture: x64
capabilities: 'internetClient'

View File

@ -14,8 +14,8 @@ import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
var settingsEnglishDark = Settings(Locale("en", ''), Opaque.dark);
var settingsEnglishLight = Settings(Locale("en", ''), Opaque.light);
var settingsEnglishDark = Settings(Locale("en", ''), OpaqueDark());
var settingsEnglishLight = Settings(Locale("en", ''), OpaqueLight());
ChangeNotifierProvider<Settings> getSettingsEnglishDark() => ChangeNotifierProvider.value(value: settingsEnglishDark);
void main() {

View File

@ -14,8 +14,8 @@ import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
var settingsEnglishDark = Settings(Locale("en", ''), Opaque.dark);
var settingsEnglishLight = Settings(Locale("en", ''), Opaque.light);
var settingsEnglishDark = Settings(Locale("en", ''), OpaqueDark());
var settingsEnglishLight = Settings(Locale("en", ''), OpaqueLight());
ChangeNotifierProvider<Settings> getSettingsEnglishDark() => ChangeNotifierProvider.value(value: settingsEnglishDark);
void main() {

View File

@ -14,8 +14,8 @@ import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
var settingsEnglishDark = Settings(Locale("en", ''), Opaque.dark);
var settingsEnglishLight = Settings(Locale("en", ''), Opaque.light);
var settingsEnglishDark = Settings(Locale("en", ''), OpaqueDark());
var settingsEnglishLight = Settings(Locale("en", ''), OpaqueLight());
ChangeNotifierProvider<Settings> getSettingsEnglishDark() => ChangeNotifierProvider.value(value: settingsEnglishDark);
String file(String slug) {

View File

@ -14,8 +14,8 @@ import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
var settingsEnglishDark = Settings(Locale("en", ''), Opaque.dark);
var settingsEnglishLight = Settings(Locale("en", ''), Opaque.light);
var settingsEnglishDark = Settings(Locale("en", ''), OpaqueDark());
var settingsEnglishLight = Settings(Locale("en", ''), OpaqueLight());
ChangeNotifierProvider<Settings> getSettingsEnglishDark() => ChangeNotifierProvider.value(value: settingsEnglishDark);
String file(String slug) {

View File

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

View File

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

BIN
windows/nsis/brand_side.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

6
windows/nsis/create.sh Normal file
View File

@ -0,0 +1,6 @@
- cp nsis/cwtch-installer.nsi deploy/
- cd deploy
- makensis -V3 cwtch-installer.nsi
- export BUILDDATE=`date +%G-%m-%d-%H-%M`
- export FILENAME=cwtch-installer-$BUILDDATE.exe
- mv cwtch-installer.exe $FILENAME

View File

@ -0,0 +1,92 @@
; USAGE: Run in ui/deploy, requires the output be in 'windows' directory
!include "MUI2.nsh"
; General settings ----------------------------
Name "Cwtch"
; !define MUI_BRANDINGTEXT "SIG Beta Ver. 1.0"
Unicode True
# define the name of the installer
Outfile "cwtch-installer.exe"
# For removing Start Menu shortcut in Windows 7
#RequestExecutionLevel user
RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on)
# define the directory to install to, the desktop in this case as specified
# by the predefined $DESKTOP variable
InstallDir "$PROGRAMFILES\Cwtch"
;Get installation folder from registry if available
InstallDirRegKey HKCU "Software\Cwtch" "installLocation"
; MUI Interface -----------------------------
!define MUI_INSTALLCOLORS "DFB9DE 281831"
; 128x128, 32bit
!define MUI_ICON "../runner/resources/knot_128.ico"
!define MUI_HEADERIMAGE
!define MUI_HEADERIMAGE_BITMAP "cwtch_title.bmp"
!define MUI_TEXTCOLOR "350052"
!define MUI_WELCOMEFINISHPAGE_BITMAP "brand_side.bmp"
!define MUI_WELCOMEFINISHPAGE_BITMAP_STRETCH NoStretchNoCrop
!define MUI_INSTFILESPAGE_COLORS "DFB9DE 281831"
!define MUI_INSTFILESPAGE_PROGRESSBAR "colored"
!define MUI_FINISHPAGE_NOAUTOCLOSE
ShowInstDetails show
; Pages --------
!define MUI_WELCOMEPAGE_TITLE "Welcome to the Cwtch installer"
!define MUI_WELCOMEPAGE_TEXT "Cwtch (pronounced: kutch) is a Welsh word roughly meaning 'a hug that creates a safe space'$\n$\n\
Cwtch is a platform for building consentful, decentralized, untrusted infrastructure using metadata resistant group communication applications. Currently there is a selfnamed instant messaging prototype app that is driving development and testing. Many Further apps are planned as the platform matures."
!define MUI_FINISHPAGE_TITLE "Enjoy Cwtch"
!define MUI_FINISHPAGE_RUN $INSTDIR/ui.exe
!define MUI_FINISHPAGE_TEXT "You can keep up-to-date on Cwtch and report any issues you have at https://cwtch.im"
!define MUI_FINISHPAGE_LINK "https://cwtch.im"
!define MUI_FINISHPAGE_LINK_LOCATION "https://cwtch.im"
!define MUI_FINISHPAGE_LINK_COLOR "D01972"
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "../../LICENSE"
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
; Languages --------------------------------
!insertmacro MUI_LANGUAGE "English"
# default section
Section
# define the output path for this file
SetOutPath $INSTDIR
# define what to install and place it in the output path
# Filler for .sh to populate with contents of deploy/windows
#FILESLISTSTART
FILE /r "..\..\build\windows\runner\Release\"
#FILESLISTEND
# create a shortcut in the start menu programs directory
CreateDirectory "$SMPROGRAMS\Cwtch"
CreateShortcut "$SMPROGRAMS\Cwtch\Cwtch.lnk" "$INSTDIR\cwtch.exe" "" "$INSTDIR\cwtch.ico"
;Store installation folder
WriteRegStr HKCU "Software\Cwtch" "installLocation" $INSTDIR
SectionEnd

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB