Compare commits

..

1 Commits
trunk ... quote

Author SHA1 Message Date
Sarah Jamie Lewis a784ac1937 UI state fixes
Fix #89
Fix #75
2021-07-08 13:29:42 -07:00
74 changed files with 826 additions and 1152 deletions

View File

@ -24,13 +24,13 @@ steps:
- git checkout $DRONE_COMMIT
- name: fetch
image: cirrusci/flutter:2.5.0-6.0.pre
image: cirrusci/flutter:dev
volumes:
- name: deps
path: /root/.pub-cache
commands:
- ./fetch-tor.sh
- echo `git describe --tags --abbrev=1` > VERSION
- echo `git describe --tags` > VERSION
- echo `date +%G-%m-%d-%H-%M` > BUILDDATE
- flutter pub get
- mkdir deploy
@ -47,7 +47,7 @@ steps:
# #Todo: fix all the lint errors and add `-set_exit_status` above to enforce linting
- name: build-linux
image: openpriv/flutter-desktop:linux-fdev2.5rc
image: openpriv/flutter-desktop:linux-dev
volumes:
- name: deps
path: /root/.pub-cache
@ -61,7 +61,7 @@ steps:
- rm -r cwtch
- name: test-build-android
image: cirrusci/flutter:2.5.0-6.0.pre
image: cirrusci/flutter:dev
when:
event: pull_request
volumes:
@ -71,7 +71,7 @@ steps:
- flutter build apk --debug
- name: build-android
image: cirrusci/flutter:2.5.0-6.0.pre
image: cirrusci/flutter:dev
when:
event: push
environment:
@ -95,7 +95,7 @@ steps:
#- cp build/app/outputs/flutter-apk/app-debug.apk deploy/android
- name: widget-tests
image: cirrusci/flutter:2.5.0-6.0.pre
image: cirrusci/flutter:dev
volumes:
- name: deps
path: /root/.pub-cache
@ -126,7 +126,7 @@ steps:
- mv ./../sha256s.txt .
- cd ..
# TODO: do deployment once files actaully compile
- scp -r -o StrictHostKeyChecking=no -i ~/id_rsa $DIR buildfiles@build.openprivacy.ca:/home/buildfiles/buildfiles/
- scp -r -o StrictHostKeyChecking=no -i ~/id_rsa $DIR buildfiles@openprivacy.ca:/home/buildfiles/buildfiles/
- name: notify-email
image: drillster/drone-email
@ -175,7 +175,7 @@ clone:
steps:
- name: clone
image: openpriv/flutter-desktop:windows-sdk30-fdev2.5rc
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
environment:
buildbot_key_b64:
from_secret: buildbot_key_b64
@ -193,16 +193,16 @@ steps:
- git checkout $Env:DRONE_COMMIT
- name: fetch
image: openpriv/flutter-desktop:windows-sdk30-fdev2.5rc
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
commands:
- powershell -command "Invoke-WebRequest -Uri https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-win64-0.4.6.5.zip -OutFile tor.zip"
- powershell -command "if ((Get-FileHash tor.zip -Algorithm sha512).Hash -ne '7917561a7a063440a1ddfa9cb544ab9ffd09de84cea3dd66e3cc9cd349dd9f85b74a522ec390d7a974bc19b424c4d53af60e57bbc47e763d13cab6a203c4592f' ) { Write-Error 'tor.zip sha512sum mismatch' }"
- git describe --tags --abbrev=1 > VERSION
- powershell -command "Invoke-WebRequest -Uri https://dist.torproject.org/torbrowser/10.0.18/tor-win64-0.4.5.9.zip -OutFile tor.zip"
- powershell -command "if ((Get-FileHash tor.zip -Algorithm sha512).Hash -ne '72764eb07ad8ab511603aba0734951ca003989f5f4686af91ba220217b4a8a4bcc5f571b59f52c847932f6efedf847b111621983050fcddbb8099d43ca66fb07' ) { Write-Error 'tor.zip sha512sum mismatch' }"
- 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.5rc
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
commands:
- flutter pub get
- $Env:version += type .\VERSION
@ -213,9 +213,9 @@ steps:
# 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.30133\x64\Microsoft.VC142.CRT\vcruntime140.dll $Env:releasedir
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30133\x64\Microsoft.VC142.CRT\vcruntime140_1.dll $Env:releasedir
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30133\x64\Microsoft.VC142.CRT\msvcp140.dll $Env:releasedir
- 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
- copy README.md $Env:releasedir\
- copy windows\*.bat $Env:releasedir\
- powershell -command "Expand-Archive -Path tor.zip -DestinationPath $Env:releasedir\Tor"
@ -258,7 +258,7 @@ steps:
- move *.sha512 deploy\$Env:builddir
- name: deploy-windows
image: openpriv/flutter-desktop:windows-sdk30-fdev2.5rc
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
when:
event: push
status: [ success ]
@ -268,7 +268,7 @@ steps:
commands:
- echo $Env:BUILDFILES_KEY > id_rsab64
- certutil -decode id_rsab64 id_rsa
- 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@openprivacy.ca:/home/buildfiles/buildfiles/
trigger:
repo: cwtch.im/cwtch-ui

View File

@ -1 +1 @@
v1.1.1-16-g7376218-2021-08-25-16-54
v1.0.0-29-g41ae09d-2021-07-08-20-22

View File

@ -68,7 +68,6 @@ In Lokalise, hit Download and make sure:
* Format is set to "Flutter (.arb)
* Output filename is set to `l10n/intl_%LANG_ISO%.%FORMAT%`
* Empty translations is set to "Replace with base language"
* Order "Last Update"
Build, download and unzip the output, overwriting `lib/l10n`. The next time Flwtch is built, Flutter will notice the changes and update `app_localizations.dart` accordingly (thanks to `generate:true` in `pubspec.yaml`).

View File

@ -87,7 +87,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
.setContentTitle(data.getString("Nick"))
.setContentText("New message")//todo: translate
.setLargeIcon(BitmapFactory.decodeStream(fh))
.setSmallIcon(R.mipmap.knott_transparent)
.setSmallIcon(R.mipmap.knott)
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setAutoCancel(true)
.build()
@ -240,7 +240,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
.setContentTitle(title)
.setTicker(title)
.setContentText(progress)
.setSmallIcon(R.mipmap.knott_transparent)
.setSmallIcon(R.mipmap.knott)
.setOngoing(true)
// Add the cancel action to the notification which can
// be used to cancel the worker

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -0,0 +1,399 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 24.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 500 500"
style="enable-background:new 0 0 500 500;"
xml:space="preserve"
sodipodi:docname="Cwtch_knott_white.svg"
inkscape:export-filename="/home/sarah/PARA/projects/cwtch/assets/core/knott-white.png"
inkscape:export-xdpi="98.300003"
inkscape:export-ydpi="98.300003"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><metadata
id="metadata35"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs33" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1020"
id="namedview31"
showgrid="false"
inkscape:zoom="1.3350176"
inkscape:cx="-56.414859"
inkscape:cy="254.41396"
inkscape:window-x="0"
inkscape:window-y="31"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{fill:#FFFFFF;}
.st1{fill:#010101;}
.st2{fill:#AF9CBA;}
.st3{clip-path:url(#SVGID_2_);}
.st4{clip-path:url(#SVGID_3_);}
.st5{clip-path:url(#SVGID_4_);}
.st6{clip-path:url(#SVGID_6_);}
.st7{clip-path:url(#SVGID_7_);}
.st8{clip-path:url(#SVGID_8_);}
.st9{clip-path:url(#SVGID_10_);}
.st10{clip-path:url(#SVGID_11_);}
.st11{clip-path:url(#SVGID_12_);}
.st12{clip-path:url(#SVGID_14_);}
.st13{clip-path:url(#SVGID_15_);}
.st14{clip-path:url(#SVGID_16_);}
.st15{clip-path:url(#SVGID_18_);}
.st16{clip-path:url(#SVGID_19_);}
.st17{clip-path:url(#SVGID_20_);}
.st18{clip-path:url(#SVGID_22_);}
.st19{clip-path:url(#SVGID_23_);}
.st20{clip-path:url(#SVGID_24_);}
.st21{clip-path:url(#SVGID_26_);}
.st22{clip-path:url(#SVGID_27_);}
.st23{clip-path:url(#SVGID_28_);}
.st24{clip-path:url(#SVGID_30_);}
.st25{clip-path:url(#SVGID_31_);}
.st26{clip-path:url(#SVGID_32_);}
.st27{clip-path:url(#SVGID_34_);}
.st28{clip-path:url(#SVGID_35_);}
.st29{clip-path:url(#SVGID_36_);}
.st30{clip-path:url(#SVGID_38_);}
.st31{clip-path:url(#SVGID_39_);}
.st32{clip-path:url(#SVGID_40_);}
.st33{clip-path:url(#SVGID_42_);}
.st34{clip-path:url(#SVGID_43_);}
.st35{clip-path:url(#SVGID_44_);}
.st36{clip-path:url(#SVGID_46_);}
.st37{clip-path:url(#SVGID_47_);}
.st38{clip-path:url(#SVGID_48_);}
.st39{clip-path:url(#SVGID_50_);}
.st40{clip-path:url(#SVGID_51_);}
.st41{clip-path:url(#SVGID_52_);}
.st42{clip-path:url(#SVGID_54_);}
.st43{clip-path:url(#SVGID_55_);}
.st44{clip-path:url(#SVGID_56_);}
.st45{clip-path:url(#SVGID_58_);}
.st46{clip-path:url(#SVGID_59_);}
.st47{clip-path:url(#SVGID_60_);}
.st48{clip-path:url(#SVGID_62_);}
.st49{clip-path:url(#SVGID_63_);}
.st50{clip-path:url(#SVGID_64_);}
.st51{clip-path:url(#SVGID_66_);}
.st52{clip-path:url(#SVGID_67_);}
.st53{clip-path:url(#SVGID_68_);}
.st54{clip-path:url(#SVGID_70_);}
.st55{clip-path:url(#SVGID_71_);}
.st56{clip-path:url(#SVGID_72_);}
.st57{clip-path:url(#SVGID_74_);}
.st58{clip-path:url(#SVGID_75_);}
.st59{clip-path:url(#SVGID_76_);}
.st60{clip-path:url(#SVGID_78_);}
.st61{clip-path:url(#SVGID_79_);}
.st62{clip-path:url(#SVGID_80_);}
.st63{clip-path:url(#SVGID_82_);}
.st64{clip-path:url(#SVGID_83_);}
.st65{clip-path:url(#SVGID_84_);}
.st66{clip-path:url(#SVGID_86_);}
.st67{clip-path:url(#SVGID_87_);}
.st68{clip-path:url(#SVGID_88_);}
.st69{clip-path:url(#SVGID_90_);}
.st70{clip-path:url(#SVGID_91_);}
.st71{clip-path:url(#SVGID_92_);}
.st72{clip-path:url(#SVGID_94_);}
.st73{clip-path:url(#SVGID_95_);}
.st74{clip-path:url(#SVGID_96_);}
.st75{clip-path:url(#SVGID_98_);}
.st76{clip-path:url(#SVGID_99_);}
.st77{clip-path:url(#SVGID_100_);}
.st78{clip-path:url(#SVGID_102_);}
.st79{clip-path:url(#SVGID_103_);}
.st80{clip-path:url(#SVGID_104_);}
.st81{clip-path:url(#SVGID_106_);}
.st82{clip-path:url(#SVGID_107_);}
.st83{clip-path:url(#SVGID_108_);}
.st84{clip-path:url(#SVGID_110_);}
.st85{clip-path:url(#SVGID_111_);}
.st86{clip-path:url(#SVGID_112_);}
.st87{clip-path:url(#SVGID_114_);}
.st88{clip-path:url(#SVGID_115_);}
.st89{clip-path:url(#SVGID_116_);}
.st90{clip-path:url(#SVGID_118_);}
.st91{clip-path:url(#SVGID_119_);}
.st92{clip-path:url(#SVGID_120_);}
.st93{clip-path:url(#SVGID_122_);}
.st94{clip-path:url(#SVGID_123_);}
.st95{clip-path:url(#SVGID_124_);}
.st96{clip-path:url(#SVGID_126_);}
.st97{clip-path:url(#SVGID_127_);}
.st98{clip-path:url(#SVGID_128_);}
.st99{fill:#64317C;}
.st100{clip-path:url(#SVGID_130_);}
.st101{clip-path:url(#SVGID_131_);}
.st102{clip-path:url(#SVGID_132_);}
.st103{clip-path:url(#SVGID_134_);}
.st104{clip-path:url(#SVGID_135_);}
.st105{clip-path:url(#SVGID_136_);}
.st106{clip-path:url(#SVGID_138_);}
.st107{clip-path:url(#SVGID_139_);}
.st108{clip-path:url(#SVGID_140_);}
.st109{clip-path:url(#SVGID_142_);}
.st110{clip-path:url(#SVGID_143_);}
.st111{clip-path:url(#SVGID_144_);}
.st112{clip-path:url(#SVGID_146_);}
.st113{clip-path:url(#SVGID_147_);}
.st114{clip-path:url(#SVGID_148_);}
.st115{clip-path:url(#SVGID_150_);}
.st116{clip-path:url(#SVGID_151_);}
.st117{clip-path:url(#SVGID_152_);}
.st118{clip-path:url(#SVGID_154_);}
.st119{clip-path:url(#SVGID_155_);}
.st120{clip-path:url(#SVGID_156_);}
.st121{clip-path:url(#SVGID_158_);}
.st122{clip-path:url(#SVGID_159_);}
.st123{clip-path:url(#SVGID_160_);}
.st124{clip-path:url(#SVGID_162_);}
.st125{clip-path:url(#SVGID_163_);}
.st126{clip-path:url(#SVGID_164_);}
.st127{clip-path:url(#SVGID_166_);}
.st128{clip-path:url(#SVGID_167_);}
.st129{clip-path:url(#SVGID_168_);}
.st130{clip-path:url(#SVGID_170_);}
.st131{clip-path:url(#SVGID_171_);}
.st132{clip-path:url(#SVGID_172_);}
.st133{clip-path:url(#SVGID_174_);}
.st134{clip-path:url(#SVGID_175_);}
.st135{clip-path:url(#SVGID_176_);}
.st136{clip-path:url(#SVGID_178_);}
.st137{clip-path:url(#SVGID_179_);}
.st138{clip-path:url(#SVGID_180_);}
.st139{clip-path:url(#SVGID_182_);}
.st140{clip-path:url(#SVGID_183_);}
.st141{clip-path:url(#SVGID_184_);}
.st142{clip-path:url(#SVGID_186_);}
.st143{clip-path:url(#SVGID_187_);}
.st144{clip-path:url(#SVGID_188_);}
.st145{clip-path:url(#SVGID_190_);}
.st146{clip-path:url(#SVGID_191_);}
.st147{clip-path:url(#SVGID_192_);}
.st148{clip-path:url(#SVGID_194_);}
.st149{clip-path:url(#SVGID_195_);}
.st150{clip-path:url(#SVGID_196_);}
.st151{clip-path:url(#SVGID_198_);}
.st152{clip-path:url(#SVGID_199_);}
.st153{clip-path:url(#SVGID_200_);}
.st154{clip-path:url(#SVGID_202_);}
.st155{clip-path:url(#SVGID_203_);}
.st156{clip-path:url(#SVGID_204_);}
.st157{clip-path:url(#SVGID_206_);}
.st158{clip-path:url(#SVGID_207_);}
.st159{clip-path:url(#SVGID_208_);}
.st160{clip-path:url(#SVGID_210_);}
.st161{clip-path:url(#SVGID_211_);}
.st162{clip-path:url(#SVGID_212_);}
.st163{clip-path:url(#SVGID_214_);}
.st164{clip-path:url(#SVGID_215_);}
.st165{clip-path:url(#SVGID_216_);}
.st166{clip-path:url(#SVGID_218_);}
.st167{clip-path:url(#SVGID_219_);}
.st168{clip-path:url(#SVGID_220_);}
.st169{clip-path:url(#SVGID_222_);}
.st170{clip-path:url(#SVGID_223_);}
.st171{clip-path:url(#SVGID_224_);}
.st172{clip-path:url(#SVGID_226_);}
.st173{clip-path:url(#SVGID_227_);}
.st174{clip-path:url(#SVGID_228_);}
.st175{clip-path:url(#SVGID_230_);}
.st176{clip-path:url(#SVGID_231_);}
.st177{clip-path:url(#SVGID_232_);}
.st178{clip-path:url(#SVGID_234_);}
.st179{clip-path:url(#SVGID_235_);}
.st180{clip-path:url(#SVGID_236_);}
.st181{clip-path:url(#SVGID_238_);}
.st182{clip-path:url(#SVGID_239_);}
.st183{clip-path:url(#SVGID_240_);}
.st184{clip-path:url(#SVGID_242_);}
.st185{clip-path:url(#SVGID_243_);}
.st186{clip-path:url(#SVGID_244_);}
.st187{clip-path:url(#SVGID_246_);}
.st188{clip-path:url(#SVGID_247_);}
.st189{clip-path:url(#SVGID_248_);}
.st190{clip-path:url(#SVGID_250_);}
.st191{clip-path:url(#SVGID_251_);}
.st192{clip-path:url(#SVGID_252_);}
.st193{clip-path:url(#SVGID_254_);}
.st194{clip-path:url(#SVGID_255_);}
.st195{clip-path:url(#SVGID_256_);}
</style>
<g
transform="translate(2.1186441)"
id="g61"
style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:7.4999999;stroke-miterlimit:4;stroke-dasharray:none">
<path
style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:7.4999999;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
id="path37"
d="m 465.94,209.46 c -0.72,3.49 -2.35,7.45 -4.95,12.03 l 21.16,21.16 c 2.83,-5.51 7.14,-15.16 9.47,-26.61 4.76,-23.39 -0.6,-43.02 -15.93,-58.35 -28.01,-28.01 -57.26,-30.61 -94.84,-8.43 -9.04,5.34 -18.64,12.24 -28.55,20.51 -22.58,18.86 -47.88,45.85 -75.22,80.23 17.91,22.52 35,41.95 50.83,57.79 7.26,7.26 14.4,13.91 21.27,19.81 l 18.76,-18.76 c -6.83,-5.78 -13.98,-12.41 -21.32,-19.75 -10.39,-10.39 -21.5,-22.57 -32.99,-36.19 l -2.43,-2.9 2.44,-2.89 c 21.03,-24.91 40.59,-44.8 58.14,-59.09 11.14,-9.08 21.56,-15.98 30.98,-20.53 8.14,-3.92 15.55,-6.11 22.03,-6.49 3.16,-0.19 8.05,-0.15 14,2.3 5.84,2.4 11.79,6.68 18.2,13.09 2.39,2.39 5.82,6.25 7.91,12.08 2.16,6.02 2.51,12.85 1.07,20.88 z"
class="st0" />
<path
style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:7.4999999;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
id="path39"
d="M 355.44,371.4 450,276.85 c -5.56,-8.23 -12.85,-16.22 -16.93,-20.49 l -93.37,93.37 c 6.02,7.42 11.29,14.68 15.74,21.67 z"
class="st0" />
<path
style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:7.4999999;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
id="path41"
d="m 475.69,342.31 c 15.32,-15.32 20.68,-34.95 15.93,-58.34 -3.3,-16.23 -10.57,-28.84 -11.98,-31.18 l -75.82,-75.82 c -6.95,3.64 -14.5,8.63 -22.52,14.89 l 58.13,58.14 -0.03,0.03 c 4.8,5.01 13.98,15.11 20.13,25.07 3.75,6.08 5.87,11.21 6.46,15.66 1.42,7.98 1.06,14.77 -1.09,20.77 -2.09,5.83 -5.51,9.68 -7.91,12.08 -6.83,6.83 -13.16,11.26 -19.37,13.55 -6.33,2.34 -11.55,2.09 -15.39,1.6 -5.05,-0.63 -10.62,-2.28 -16.58,-4.9 L 385.9,353.6 c 35.17,19.04 63.04,15.46 89.79,-11.29 z"
class="st0" />
<path
style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:7.4999999;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
id="path43"
d="m 311.53,35.11 c 5.83,2.09 9.68,5.51 12.08,7.91 6.83,6.83 11.26,13.16 13.55,19.37 2.34,6.33 2.09,11.55 1.6,15.39 -0.63,5.05 -2.28,10.62 -4.9,16.58 L 353.6,114.1 C 372.64,78.93 369.06,51.06 342.31,24.31 326.99,8.99 307.36,3.63 283.97,8.38 c -16.23,3.3 -28.84,10.57 -31.18,11.98 l -75.82,75.82 c 3.64,6.95 8.63,14.5 14.89,22.52 L 250,60.57 l 0.03,0.03 c 5.01,-4.8 15.11,-13.98 25.07,-20.13 6.08,-3.75 11.21,-5.87 15.66,-6.46 7.98,-1.41 14.77,-1.05 20.77,1.1 z"
class="st0" />
<path
style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:7.4999999;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
id="path45"
d="m 157.69,24.31 c -28.01,28.01 -30.61,57.26 -8.43,94.84 5.34,9.04 12.24,18.64 20.51,28.54 18.86,22.58 45.85,47.88 80.23,75.22 22.52,-17.91 41.95,-35 57.79,-50.83 7.26,-7.26 13.91,-14.4 19.81,-21.27 l -18.76,-18.76 c -5.78,6.83 -12.41,13.98 -19.75,21.32 -10.39,10.39 -22.57,21.49 -36.19,32.99 l -2.9,2.44 -2.89,-2.44 C 222.2,165.33 202.31,145.77 188.02,128.22 178.94,117.08 172.04,106.66 167.49,97.24 163.57,89.1 161.38,81.69 161,75.21 c -0.19,-3.16 -0.15,-8.05 2.3,-14 2.4,-5.84 6.68,-11.79 13.09,-18.2 2.39,-2.39 6.25,-5.82 12.08,-7.91 6.02,-2.16 12.85,-2.51 20.87,-1.07 l 0.11,0.02 c 3.49,0.72 7.45,2.35 12.03,4.95 L 242.64,17.84 C 237.13,15.01 227.48,10.7 216.02,8.37 192.64,3.63 173.01,8.99 157.69,24.31 Z"
class="st0" />
<path
style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:7.4999999;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
id="path47"
d="m 276.85,50 c -8.23,5.56 -16.22,12.85 -20.49,16.92 l 93.37,93.37 c 7.42,-6.02 14.68,-11.29 21.67,-15.74 z"
class="st0" />
<path
style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:7.4999999;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
id="path49"
d="m 24.31,342.31 c 28.01,28.01 57.26,30.61 94.84,8.43 9.04,-5.34 18.64,-12.24 28.54,-20.51 22.58,-18.86 47.88,-45.85 75.22,-80.23 -17.91,-22.52 -35,-41.95 -50.83,-57.79 -7.26,-7.26 -14.4,-13.91 -21.27,-19.81 l -18.76,18.76 c 6.83,5.78 13.98,12.41 21.32,19.75 10.39,10.39 21.5,22.57 32.99,36.19 l 2.44,2.89 -2.44,2.89 c -21.03,24.91 -40.59,44.8 -58.14,59.09 -11.14,9.08 -21.56,15.98 -30.98,20.53 -8.14,3.92 -15.55,6.11 -22.03,6.49 -3.16,0.19 -8.05,0.15 -14,-2.3 -5.84,-2.4 -11.79,-6.68 -18.2,-13.09 -2.39,-2.39 -5.82,-6.25 -7.91,-12.08 -2.16,-6.02 -2.51,-12.85 -1.07,-20.88 l 0.02,-0.11 C 34.77,287.04 36.4,283.08 39,278.5 L 17.84,257.34 c -2.83,5.51 -7.14,15.17 -9.47,26.63 -4.74,23.39 0.62,43.02 15.94,58.34 z"
class="st0" />
<path
style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:7.4999999;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
id="path51"
d="m 60.57,250 0.03,-0.03 c -4.8,-5.02 -13.98,-15.11 -20.13,-25.07 -3.75,-6.08 -5.87,-11.21 -6.46,-15.66 -1.42,-7.98 -1.06,-14.77 1.09,-20.77 2.09,-5.83 5.51,-9.68 7.91,-12.08 6.83,-6.83 13.16,-11.26 19.37,-13.55 6.33,-2.34 11.55,-2.09 15.39,-1.6 5.05,0.63 10.62,2.28 16.58,4.9 L 114.09,146.4 C 78.92,127.36 51.05,130.94 24.3,157.69 8.99,173.01 3.63,192.64 8.38,216.03 c 3.3,16.23 10.57,28.84 11.98,31.18 l 75.82,75.82 c 6.95,-3.64 14.5,-8.63 22.52,-14.89 z"
class="st0" />
<path
style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:7.4999999;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
id="path53"
d="m 66.93,243.64 93.37,-93.37 c -6.02,-7.42 -11.29,-14.68 -15.74,-21.67 L 50,223.15 c 5.57,8.23 12.85,16.23 16.93,20.49 z"
class="st0" />
<path
style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:7.4999999;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
id="path55"
d="m 342.31,475.69 c 28.01,-28.01 30.61,-57.26 8.43,-94.84 -5.34,-9.04 -12.24,-18.64 -20.51,-28.54 -18.86,-22.58 -45.85,-47.88 -80.23,-75.22 -22.52,17.91 -41.95,35 -57.79,50.83 -7.26,7.26 -13.91,14.4 -19.81,21.27 l 18.76,18.76 c 5.78,-6.83 12.41,-13.98 19.75,-21.32 10.39,-10.39 22.57,-21.49 36.19,-32.99 l 2.89,-2.44 2.89,2.44 c 24.91,21.03 44.8,40.59 59.09,58.14 9.08,11.14 15.98,21.56 20.53,30.98 3.93,8.14 6.11,15.56 6.49,22.03 0.19,3.16 0.15,8.05 -2.3,14 -2.4,5.84 -6.68,11.79 -13.09,18.2 -2.39,2.39 -6.25,5.82 -12.08,7.91 -6.02,2.16 -12.85,2.51 -20.88,1.07 l -0.11,-0.02 c -3.49,-0.72 -7.45,-2.35 -12.03,-4.95 l -21.16,21.16 c 5.5,2.83 15.15,7.13 26.59,9.46 23.41,4.76 43.05,-0.6 58.38,-15.93 z"
class="st0" />
<path
style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:7.4999999;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
id="path57"
d="M 243.64,433.07 150.27,339.7 c -7.42,6.02 -14.68,11.29 -21.67,15.74 L 223.15,450 c 8.23,-5.57 16.23,-12.85 20.49,-16.93 z"
class="st0" />
<path
style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:7.4999999;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0"
id="path59"
d="m 157.69,475.69 c 15.32,15.32 34.95,20.68 58.33,15.93 16.24,-3.3 28.85,-10.58 31.19,-11.98 l 75.82,-75.82 c -3.64,-6.95 -8.63,-14.5 -14.89,-22.52 L 250,439.43 249.97,439.4 c -5.01,4.8 -15.11,13.98 -25.07,20.13 -6.08,3.75 -11.21,5.87 -15.66,6.46 -7.98,1.42 -14.77,1.06 -20.77,-1.09 -5.83,-2.09 -9.68,-5.51 -12.08,-7.91 -6.83,-6.83 -11.26,-13.16 -13.55,-19.37 -2.34,-6.33 -2.09,-11.55 -1.6,-15.39 0.63,-5.05 2.28,-10.62 4.9,-16.58 L 146.4,385.9 c -19.04,35.17 -15.46,63.04 11.29,89.79 z"
class="st0" />
</g><g
id="g28"
transform="translate(2.1186441)">
<path
class="st0"
d="m 465.94,209.46 c -0.72,3.49 -2.35,7.45 -4.95,12.03 l 21.16,21.16 c 2.83,-5.51 7.14,-15.16 9.47,-26.61 4.76,-23.39 -0.6,-43.02 -15.93,-58.35 -28.01,-28.01 -57.26,-30.61 -94.84,-8.43 -9.04,5.34 -18.64,12.24 -28.55,20.51 -22.58,18.86 -47.88,45.85 -75.22,80.23 17.91,22.52 35,41.95 50.83,57.79 7.26,7.26 14.4,13.91 21.27,19.81 l 18.76,-18.76 c -6.83,-5.78 -13.98,-12.41 -21.32,-19.75 -10.39,-10.39 -21.5,-22.57 -32.99,-36.19 l -2.43,-2.9 2.44,-2.89 c 21.03,-24.91 40.59,-44.8 58.14,-59.09 11.14,-9.08 21.56,-15.98 30.98,-20.53 8.14,-3.92 15.55,-6.11 22.03,-6.49 3.16,-0.19 8.05,-0.15 14,2.3 5.84,2.4 11.79,6.68 18.2,13.09 2.39,2.39 5.82,6.25 7.91,12.08 2.16,6.02 2.51,12.85 1.07,20.88 z"
id="path4"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="M 355.44,371.4 450,276.85 c -5.56,-8.23 -12.85,-16.22 -16.93,-20.49 l -93.37,93.37 c 6.02,7.42 11.29,14.68 15.74,21.67 z"
id="path6"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m 475.69,342.31 c 15.32,-15.32 20.68,-34.95 15.93,-58.34 -3.3,-16.23 -10.57,-28.84 -11.98,-31.18 l -75.82,-75.82 c -6.95,3.64 -14.5,8.63 -22.52,14.89 l 58.13,58.14 -0.03,0.03 c 4.8,5.01 13.98,15.11 20.13,25.07 3.75,6.08 5.87,11.21 6.46,15.66 1.42,7.98 1.06,14.77 -1.09,20.77 -2.09,5.83 -5.51,9.68 -7.91,12.08 -6.83,6.83 -13.16,11.26 -19.37,13.55 -6.33,2.34 -11.55,2.09 -15.39,1.6 -5.05,-0.63 -10.62,-2.28 -16.58,-4.9 L 385.9,353.6 c 35.17,19.04 63.04,15.46 89.79,-11.29 z"
id="path8"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m 311.53,35.11 c 5.83,2.09 9.68,5.51 12.08,7.91 6.83,6.83 11.26,13.16 13.55,19.37 2.34,6.33 2.09,11.55 1.6,15.39 -0.63,5.05 -2.28,10.62 -4.9,16.58 L 353.6,114.1 C 372.64,78.93 369.06,51.06 342.31,24.31 326.99,8.99 307.36,3.63 283.97,8.38 c -16.23,3.3 -28.84,10.57 -31.18,11.98 l -75.82,75.82 c 3.64,6.95 8.63,14.5 14.89,22.52 L 250,60.57 l 0.03,0.03 c 5.01,-4.8 15.11,-13.98 25.07,-20.13 6.08,-3.75 11.21,-5.87 15.66,-6.46 7.98,-1.41 14.77,-1.05 20.77,1.1 z"
id="path10"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m 157.69,24.31 c -28.01,28.01 -30.61,57.26 -8.43,94.84 5.34,9.04 12.24,18.64 20.51,28.54 18.86,22.58 45.85,47.88 80.23,75.22 22.52,-17.91 41.95,-35 57.79,-50.83 7.26,-7.26 13.91,-14.4 19.81,-21.27 l -18.76,-18.76 c -5.78,6.83 -12.41,13.98 -19.75,21.32 -10.39,10.39 -22.57,21.49 -36.19,32.99 l -2.9,2.44 -2.89,-2.44 C 222.2,165.33 202.31,145.77 188.02,128.22 178.94,117.08 172.04,106.66 167.49,97.24 163.57,89.1 161.38,81.69 161,75.21 c -0.19,-3.16 -0.15,-8.05 2.3,-14 2.4,-5.84 6.68,-11.79 13.09,-18.2 2.39,-2.39 6.25,-5.82 12.08,-7.91 6.02,-2.16 12.85,-2.51 20.87,-1.07 l 0.11,0.02 c 3.49,0.72 7.45,2.35 12.03,4.95 L 242.64,17.84 C 237.13,15.01 227.48,10.7 216.02,8.37 192.64,3.63 173.01,8.99 157.69,24.31 Z"
id="path12"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m 276.85,50 c -8.23,5.56 -16.22,12.85 -20.49,16.92 l 93.37,93.37 c 7.42,-6.02 14.68,-11.29 21.67,-15.74 z"
id="path14"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m 24.31,342.31 c 28.01,28.01 57.26,30.61 94.84,8.43 9.04,-5.34 18.64,-12.24 28.54,-20.51 22.58,-18.86 47.88,-45.85 75.22,-80.23 -17.91,-22.52 -35,-41.95 -50.83,-57.79 -7.26,-7.26 -14.4,-13.91 -21.27,-19.81 l -18.76,18.76 c 6.83,5.78 13.98,12.41 21.32,19.75 10.39,10.39 21.5,22.57 32.99,36.19 l 2.44,2.89 -2.44,2.89 c -21.03,24.91 -40.59,44.8 -58.14,59.09 -11.14,9.08 -21.56,15.98 -30.98,20.53 -8.14,3.92 -15.55,6.11 -22.03,6.49 -3.16,0.19 -8.05,0.15 -14,-2.3 -5.84,-2.4 -11.79,-6.68 -18.2,-13.09 -2.39,-2.39 -5.82,-6.25 -7.91,-12.08 -2.16,-6.02 -2.51,-12.85 -1.07,-20.88 l 0.02,-0.11 C 34.77,287.04 36.4,283.08 39,278.5 L 17.84,257.34 c -2.83,5.51 -7.14,15.17 -9.47,26.63 -4.74,23.39 0.62,43.02 15.94,58.34 z"
id="path16"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m 60.57,250 0.03,-0.03 c -4.8,-5.02 -13.98,-15.11 -20.13,-25.07 -3.75,-6.08 -5.87,-11.21 -6.46,-15.66 -1.42,-7.98 -1.06,-14.77 1.09,-20.77 2.09,-5.83 5.51,-9.68 7.91,-12.08 6.83,-6.83 13.16,-11.26 19.37,-13.55 6.33,-2.34 11.55,-2.09 15.39,-1.6 5.05,0.63 10.62,2.28 16.58,4.9 L 114.09,146.4 C 78.92,127.36 51.05,130.94 24.3,157.69 8.99,173.01 3.63,192.64 8.38,216.03 c 3.3,16.23 10.57,28.84 11.98,31.18 l 75.82,75.82 c 6.95,-3.64 14.5,-8.63 22.52,-14.89 z"
id="path18"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m 66.93,243.64 93.37,-93.37 c -6.02,-7.42 -11.29,-14.68 -15.74,-21.67 L 50,223.15 c 5.57,8.23 12.85,16.23 16.93,20.49 z"
id="path20"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m 342.31,475.69 c 28.01,-28.01 30.61,-57.26 8.43,-94.84 -5.34,-9.04 -12.24,-18.64 -20.51,-28.54 -18.86,-22.58 -45.85,-47.88 -80.23,-75.22 -22.52,17.91 -41.95,35 -57.79,50.83 -7.26,7.26 -13.91,14.4 -19.81,21.27 l 18.76,18.76 c 5.78,-6.83 12.41,-13.98 19.75,-21.32 10.39,-10.39 22.57,-21.49 36.19,-32.99 l 2.89,-2.44 2.89,2.44 c 24.91,21.03 44.8,40.59 59.09,58.14 9.08,11.14 15.98,21.56 20.53,30.98 3.93,8.14 6.11,15.56 6.49,22.03 0.19,3.16 0.15,8.05 -2.3,14 -2.4,5.84 -6.68,11.79 -13.09,18.2 -2.39,2.39 -6.25,5.82 -12.08,7.91 -6.02,2.16 -12.85,2.51 -20.88,1.07 l -0.11,-0.02 c -3.49,-0.72 -7.45,-2.35 -12.03,-4.95 l -21.16,21.16 c 5.5,2.83 15.15,7.13 26.59,9.46 23.41,4.76 43.05,-0.6 58.38,-15.93 z"
id="path22"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="M 243.64,433.07 150.27,339.7 c -7.42,6.02 -14.68,11.29 -21.67,15.74 L 223.15,450 c 8.23,-5.57 16.23,-12.85 20.49,-16.93 z"
id="path24"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m 157.69,475.69 c 15.32,15.32 34.95,20.68 58.33,15.93 16.24,-3.3 28.85,-10.58 31.19,-11.98 l 75.82,-75.82 c -3.64,-6.95 -8.63,-14.5 -14.89,-22.52 L 250,439.43 249.97,439.4 c -5.01,4.8 -15.11,13.98 -25.07,20.13 -6.08,3.75 -11.21,5.87 -15.66,6.46 -7.98,1.42 -14.77,1.06 -20.77,-1.09 -5.83,-2.09 -9.68,-5.51 -12.08,-7.91 -6.83,-6.83 -11.26,-13.16 -13.55,-19.37 -2.34,-6.33 -2.09,-11.55 -1.6,-15.39 0.63,-5.05 2.28,-10.62 4.9,-16.58 L 146.4,385.9 c -19.04,35.17 -15.46,63.04 11.29,89.79 z"
id="path26"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -1,99 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 500 500" style="enable-background:new 0 0 500 500;" xml:space="preserve">
<style type="text/css">
.st0{fill:#242425;}
.st1{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M436.2,215c-0.6,3-2,6.4-4.3,10.4l18.2,18.2c2.4-4.8,6.2-13.1,8.2-22.9c4.1-20.2-0.5-37.1-13.7-50.3
c-24.2-24.2-49.4-26.4-81.8-7.3c-7.8,4.6-16.1,10.6-24.6,17.7c-19.5,16.3-41.3,39.5-64.9,69.2c15.4,19.4,30.2,36.2,43.8,49.8
c6.3,6.3,12.4,12,18.3,17.1l16.2-16.2c-5.9-5-12.1-10.7-18.4-17c-9-9-18.5-19.5-28.4-31.2l-2.1-2.5l2.1-2.5
c18.1-21.5,35-38.6,50.1-50.9c9.6-7.8,18.6-13.8,26.7-17.7c7-3.4,13.4-5.3,19-5.6c2.7-0.2,6.9-0.1,12.1,2c5,2.1,10.2,5.8,15.7,11.3
c2.1,2.1,5,5.4,6.8,10.4C437.1,202.1,437.5,208,436.2,215L436.2,215z"/>
<path class="st0" d="M340.9,354.7l81.5-81.5c-4.8-7.1-11.1-14-14.6-17.7L327.3,336C332.5,342.4,337.1,348.6,340.9,354.7z"/>
<path class="st0" d="M444.6,329.6c13.2-13.2,17.8-30.1,13.7-50.3c-2.8-14-9.1-24.9-10.3-26.9L382.6,187c-6,3.1-12.5,7.4-19.4,12.8
l50.1,50.1l0,0c4.1,4.3,12.1,13,17.4,21.6c3.2,5.2,5.1,9.7,5.6,13.5c1.2,6.9,0.9,12.7-0.9,17.9c-1.8,5-4.8,8.3-6.8,10.4
c-5.9,5.9-11.3,9.7-16.7,11.7c-5.5,2-10,1.8-13.3,1.4c-4.4-0.5-9.2-2-14.3-4.2l-17,17C397.5,355.7,421.5,352.7,444.6,329.6z"/>
<path class="st0" d="M303.1,64.7c5,1.8,8.3,4.8,10.4,6.8c5.9,5.9,9.7,11.3,11.7,16.7c2,5.5,1.8,10,1.4,13.3
c-0.5,4.4-2,9.2-4.2,14.3l17,17c16.4-30.3,13.3-54.4-9.7-77.4c-13.2-13.2-30.1-17.8-50.3-13.7c-14,2.8-24.9,9.1-26.9,10.3
L187,117.4c3.1,6,7.4,12.5,12.8,19.4L250,86.7l0,0c4.3-4.1,13-12.1,21.6-17.4c5.2-3.2,9.7-5.1,13.5-5.6
C292,62.6,297.9,62.9,303.1,64.7z"/>
<path class="st0" d="M170.4,55.4c-24.2,24.2-26.4,49.4-7.3,81.8c4.6,7.8,10.6,16.1,17.7,24.6c16.3,19.5,39.5,41.3,69.2,64.9
c19.4-15.4,36.2-30.2,49.8-43.8c6.3-6.3,12-12.4,17.1-18.3l-16.2-16.2c-5,5.9-10.7,12.1-17,18.4c-9,9-19.5,18.5-31.2,28.4l-2.5,2.1
l-2.5-2.1c-21.5-18.1-38.6-35-50.9-50.1c-7.8-9.6-13.8-18.6-17.7-26.7c-3.4-7-5.3-13.4-5.6-19c-0.2-2.7-0.1-6.9,2-12.1
c2.1-5,5.8-10.2,11.3-15.7c2.1-2.1,5.4-5,10.4-6.8c5.2-1.9,11.1-2.2,18-0.9l0.1,0c3,0.6,6.4,2,10.4,4.3l18.2-18.2
c-4.8-2.4-13.1-6.2-23-8.2C200.5,37.6,183.6,42.2,170.4,55.4z"/>
<path class="st0" d="M273.2,77.6c-7.1,4.8-14,11.1-17.7,14.6l80.5,80.5c6.4-5.2,12.7-9.7,18.7-13.6L273.2,77.6z"/>
<path class="st0" d="M55.4,329.6c24.2,24.2,49.4,26.4,81.8,7.3c7.8-4.6,16.1-10.6,24.6-17.7c19.5-16.3,41.3-39.5,64.9-69.2
c-15.4-19.4-30.2-36.2-43.8-49.8c-6.3-6.3-12.4-12-18.3-17.1l-16.2,16.2c5.9,5,12.1,10.7,18.4,17c9,9,18.5,19.5,28.4,31.2l2.1,2.5
l-2.1,2.5c-18.1,21.5-35,38.6-50.1,50.9c-9.6,7.8-18.6,13.8-26.7,17.7c-7,3.4-13.4,5.3-19,5.6c-2.7,0.2-6.9,0.1-12.1-2
c-5-2.1-10.2-5.8-15.7-11.3c-2.1-2.1-5-5.4-6.8-10.4c-1.9-5.2-2.2-11.1-0.9-18l0-0.1c0.6-3,2-6.4,4.3-10.4l-18.2-18.2
c-2.4,4.8-6.2,13.1-8.2,23C37.6,299.5,42.2,316.4,55.4,329.6z"/>
<path class="st0" d="M86.7,250L86.7,250c-4.1-4.4-12-13.1-17.3-21.6c-3.2-5.2-5.1-9.7-5.6-13.5c-1.2-6.9-0.9-12.7,0.9-17.9
c1.8-5,4.8-8.3,6.8-10.4c5.9-5.9,11.3-9.7,16.7-11.7c5.5-2,10-1.8,13.3-1.4c4.4,0.5,9.2,2,14.3,4.2l17-17
c-30.3-16.4-54.4-13.3-77.4,9.7c-13.2,13.2-17.8,30.1-13.7,50.3c2.8,14,9.1,24.9,10.3,26.9l65.4,65.4c6-3.1,12.5-7.4,19.4-12.8
L86.7,250z"/>
<path class="st0" d="M92.2,244.5l80.5-80.5c-5.2-6.4-9.7-12.7-13.6-18.7l-81.5,81.5C82.4,233.9,88.6,240.8,92.2,244.5z"/>
<path class="st0" d="M329.6,444.6c24.2-24.2,26.4-49.4,7.3-81.8c-4.6-7.8-10.6-16.1-17.7-24.6c-16.3-19.5-39.5-41.3-69.2-64.9
c-19.4,15.4-36.2,30.2-49.8,43.8c-6.3,6.3-12,12.4-17.1,18.3l16.2,16.2c5-5.9,10.7-12.1,17-18.4c9-9,19.5-18.5,31.2-28.4l2.5-2.1
l2.5,2.1c21.5,18.1,38.6,35,50.9,50.1c7.8,9.6,13.8,18.6,17.7,26.7c3.4,7,5.3,13.4,5.6,19c0.2,2.7,0.1,6.9-2,12.1
c-2.1,5-5.8,10.2-11.3,15.7c-2.1,2.1-5.4,5-10.4,6.8c-5.2,1.9-11.1,2.2-18,0.9l-0.1,0c-3-0.6-6.4-2-10.4-4.3l-18.2,18.2
c4.7,2.4,13.1,6.1,22.9,8.2C299.4,462.4,316.4,457.8,329.6,444.6z"/>
<path class="st0" d="M244.5,407.8L164,327.3c-6.4,5.2-12.7,9.7-18.7,13.6l81.5,81.5C233.9,417.6,240.8,411.4,244.5,407.8z"/>
<path class="st0" d="M483.4,250c2.6-6.3,5.3-14,7.1-22.7c6.3-30.8-1.8-59.2-22.7-80.1c-19.7-19.7-41.7-29.7-65.5-29.7
c-7.5,0-15.1,1-22.8,3c2.9-11.3,3.7-22.3,2.3-33.2c-2.5-19.8-12.3-38.3-29-55.1C336.6,15.9,315.9,7.4,293,7.4
c-6.6,0-13.4,0.7-20.2,2.1c-8.7,1.8-16.5,4.5-22.7,7.1c-6.3-2.6-14.1-5.3-22.8-7.1c-6.9-1.4-13.7-2.1-20.2-2.1
c-22.9,0-43.6,8.6-59.9,24.8c-17.6,17.6-27.5,37.2-29.4,58.1c-0.9,9.9,0,19.9,2.7,30.2c-7.7-2-15.3-3-22.8-3
c-23.8,0-45.8,10-65.5,29.7c-20.9,20.9-28.9,49.3-22.7,80.1c1.8,8.7,4.5,16.5,7.1,22.7c-2.6,6.3-5.3,14.1-7.1,22.8
c-6.2,30.8,1.8,59.2,22.7,80.1c19.7,19.7,41.7,29.7,65.5,29.7c0,0,0,0,0,0c7.5,0,15.1-1,22.8-3c-2.9,11.3-3.7,22.3-2.3,33.2
c2.5,19.8,12.3,38.3,29,55.1c16.2,16.2,36.9,24.8,59.8,24.8c0,0,0,0,0,0c6.6,0,13.4-0.7,20.2-2.1c8.7-1.8,16.5-4.4,22.7-7.1
c6.3,2.6,14,5.3,22.7,7.1c6.9,1.4,13.7,2.1,20.3,2.1c0,0,0,0,0,0c22.9,0,43.6-8.6,59.9-24.8c17.6-17.6,27.5-37.2,29.4-58.1
c0.9-9.9,0-19.9-2.7-30.2c7.7,2,15.3,3,22.8,3c0,0,0,0,0,0c23.8,0,45.8-10,65.5-29.7c20.9-20.9,29-49.3,22.7-80.1
C488.8,264,486.1,256.3,483.4,250z"/>
<path class="st0" d="M170.4,444.6c13.2,13.2,30.1,17.8,50.3,13.7c14-2.8,24.9-9.1,26.9-10.3l65.4-65.4c-3.1-6-7.4-12.5-12.8-19.4
L250,413.3l0,0c-4.3,4.1-13,12.1-21.6,17.4c-5.2,3.2-9.7,5.1-13.5,5.6c-6.9,1.2-12.7,0.9-17.9-0.9c-5-1.8-8.3-4.8-10.4-6.8
c-5.9-5.9-9.7-11.3-11.7-16.7c-2-5.5-1.8-10-1.4-13.3c0.5-4.4,2-9.2,4.2-14.3l-17-17C144.3,397.5,147.3,421.5,170.4,444.6z"/>
</g>
<g>
<path class="st1" d="M436.2,215c-0.6,3-2,6.4-4.3,10.4l18.2,18.2c2.4-4.8,6.2-13.1,8.2-22.9c4.1-20.2-0.5-37.1-13.7-50.3
c-24.2-24.2-49.4-26.4-81.8-7.3c-7.8,4.6-16.1,10.6-24.6,17.7c-19.5,16.3-41.3,39.5-64.9,69.2c15.4,19.4,30.2,36.2,43.8,49.8
c6.3,6.3,12.4,12,18.3,17.1l16.2-16.2c-5.9-5-12.1-10.7-18.4-17c-9-9-18.5-19.5-28.4-31.2l-2.1-2.5l2.1-2.5
c18.1-21.5,35-38.6,50.1-50.9c9.6-7.8,18.6-13.8,26.7-17.7c7-3.4,13.4-5.3,19-5.6c2.7-0.2,6.9-0.1,12.1,2c5,2.1,10.2,5.8,15.7,11.3
c2.1,2.1,5,5.4,6.8,10.4C437.1,202.1,437.5,208,436.2,215L436.2,215z"/>
<path class="st1" d="M340.9,354.7l81.5-81.5c-4.8-7.1-11.1-14-14.6-17.7L327.3,336C332.5,342.4,337.1,348.6,340.9,354.7z"/>
<path class="st1" d="M444.6,329.6c13.2-13.2,17.8-30.1,13.7-50.3c-2.8-14-9.1-24.9-10.3-26.9L382.6,187c-6,3.1-12.5,7.4-19.4,12.8
l50.1,50.1l0,0c4.1,4.3,12.1,13,17.4,21.6c3.2,5.2,5.1,9.7,5.6,13.5c1.2,6.9,0.9,12.7-0.9,17.9c-1.8,5-4.8,8.3-6.8,10.4
c-5.9,5.9-11.3,9.7-16.7,11.7c-5.5,2-10,1.8-13.3,1.4c-4.4-0.5-9.2-2-14.3-4.2l-17,17C397.5,355.7,421.5,352.7,444.6,329.6z"/>
<path class="st1" d="M303.1,64.7c5,1.8,8.3,4.8,10.4,6.8c5.9,5.9,9.7,11.3,11.7,16.7c2,5.5,1.8,10,1.4,13.3
c-0.5,4.4-2,9.2-4.2,14.3l17,17c16.4-30.3,13.3-54.4-9.7-77.4c-13.2-13.2-30.1-17.8-50.3-13.7c-14,2.8-24.9,9.1-26.9,10.3
L187,117.4c3.1,6,7.4,12.5,12.8,19.4L250,86.7l0,0c4.3-4.1,13-12.1,21.6-17.4c5.2-3.2,9.7-5.1,13.5-5.6
C292,62.6,297.9,62.9,303.1,64.7z"/>
<path class="st1" d="M170.4,55.4c-24.2,24.2-26.4,49.4-7.3,81.8c4.6,7.8,10.6,16.1,17.7,24.6c16.3,19.5,39.5,41.3,69.2,64.9
c19.4-15.4,36.2-30.2,49.8-43.8c6.3-6.3,12-12.4,17.1-18.3l-16.2-16.2c-5,5.9-10.7,12.1-17,18.4c-9,9-19.5,18.5-31.2,28.4l-2.5,2.1
l-2.5-2.1c-21.5-18.1-38.6-35-50.9-50.1c-7.8-9.6-13.8-18.6-17.7-26.7c-3.4-7-5.3-13.4-5.6-19c-0.2-2.7-0.1-6.9,2-12.1
c2.1-5,5.8-10.2,11.3-15.7c2.1-2.1,5.4-5,10.4-6.8c5.2-1.9,11.1-2.2,18-0.9l0.1,0c3,0.6,6.4,2,10.4,4.3l18.2-18.2
c-4.8-2.4-13.1-6.2-23-8.2C200.5,37.6,183.6,42.2,170.4,55.4z"/>
<path class="st1" d="M273.2,77.6c-7.1,4.8-14,11.1-17.7,14.6l80.5,80.5c6.4-5.2,12.7-9.7,18.7-13.6L273.2,77.6z"/>
<path class="st1" d="M55.4,329.6c24.2,24.2,49.4,26.4,81.8,7.3c7.8-4.6,16.1-10.6,24.6-17.7c19.5-16.3,41.3-39.5,64.9-69.2
c-15.4-19.4-30.2-36.2-43.8-49.8c-6.3-6.3-12.4-12-18.3-17.1l-16.2,16.2c5.9,5,12.1,10.7,18.4,17c9,9,18.5,19.5,28.4,31.2l2.1,2.5
l-2.1,2.5c-18.1,21.5-35,38.6-50.1,50.9c-9.6,7.8-18.6,13.8-26.7,17.7c-7,3.4-13.4,5.3-19,5.6c-2.7,0.2-6.9,0.1-12.1-2
c-5-2.1-10.2-5.8-15.7-11.3c-2.1-2.1-5-5.4-6.8-10.4c-1.9-5.2-2.2-11.1-0.9-18l0-0.1c0.6-3,2-6.4,4.3-10.4l-18.2-18.2
c-2.4,4.8-6.2,13.1-8.2,23C37.6,299.5,42.2,316.4,55.4,329.6z"/>
<path class="st1" d="M86.7,250L86.7,250c-4.1-4.4-12-13.1-17.3-21.6c-3.2-5.2-5.1-9.7-5.6-13.5c-1.2-6.9-0.9-12.7,0.9-17.9
c1.8-5,4.8-8.3,6.8-10.4c5.9-5.9,11.3-9.7,16.7-11.7c5.5-2,10-1.8,13.3-1.4c4.4,0.5,9.2,2,14.3,4.2l17-17
c-30.3-16.4-54.4-13.3-77.4,9.7c-13.2,13.2-17.8,30.1-13.7,50.3c2.8,14,9.1,24.9,10.3,26.9l65.4,65.4c6-3.1,12.5-7.4,19.4-12.8
L86.7,250z"/>
<path class="st1" d="M92.2,244.5l80.5-80.5c-5.2-6.4-9.7-12.7-13.6-18.7l-81.5,81.5C82.4,233.9,88.6,240.8,92.2,244.5z"/>
<path class="st1" d="M329.6,444.6c24.2-24.2,26.4-49.4,7.3-81.8c-4.6-7.8-10.6-16.1-17.7-24.6c-16.3-19.5-39.5-41.3-69.2-64.9
c-19.4,15.4-36.2,30.2-49.8,43.8c-6.3,6.3-12,12.4-17.1,18.3l16.2,16.2c5-5.9,10.7-12.1,17-18.4c9-9,19.5-18.5,31.2-28.4l2.5-2.1
l2.5,2.1c21.5,18.1,38.6,35,50.9,50.1c7.8,9.6,13.8,18.6,17.7,26.7c3.4,7,5.3,13.4,5.6,19c0.2,2.7,0.1,6.9-2,12.1
c-2.1,5-5.8,10.2-11.3,15.7c-2.1,2.1-5.4,5-10.4,6.8c-5.2,1.9-11.1,2.2-18,0.9l-0.1,0c-3-0.6-6.4-2-10.4-4.3l-18.2,18.2
c4.7,2.4,13.1,6.1,22.9,8.2C299.4,462.4,316.4,457.8,329.6,444.6z"/>
<path class="st1" d="M244.5,407.8L164,327.3c-6.4,5.2-12.7,9.7-18.7,13.6l81.5,81.5C233.9,417.6,240.8,411.4,244.5,407.8z"/>
<path class="st1" d="M170.4,444.6c13.2,13.2,30.1,17.8,50.3,13.7c14-2.8,24.9-9.1,26.9-10.3l65.4-65.4c-3.1-6-7.4-12.5-12.8-19.4
L250,413.3l0,0c-4.3,4.1-13,12.1-21.6,17.4c-5.2,3.2-9.7,5.1-13.5,5.6c-6.9,1.2-12.7,0.9-17.9-0.9c-5-1.8-8.3-4.8-10.4-6.8
c-5.9-5.9-9.7-11.3-11.7-16.7c-2-5.5-1.8-10-1.4-13.3c0.5-4.4,2-9.2,4.2-14.3l-17-17C144.3,397.5,147.3,421.5,170.4,444.6z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 9.7 KiB

BIN
cwtch.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -1,6 +1,6 @@
Invoke-WebRequest -Uri https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-win64-0.4.6.5.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 '7917561a7a063440a1ddfa9cb544ab9ffd09de84cea3dd66e3cc9cd349dd9f85b74a522ec390d7a974bc19b424c4d53af60e57bbc47e763d13cab6a203c4592f' ) { 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

@ -49,7 +49,8 @@ class CwtchNotifier {
nickname: data["nick"],
status: data["status"],
imagePath: data["picture"],
authorization: stringToContactAuthorization(data["authorization"]),
isBlocked: data["authorization"] == "blocked",
isInvitation: data["authorization"] == "unknown",
savePeerHistory: data["saveConversationHistory"] == null ? "DeleteHistoryConfirmed" : data["saveConversationHistory"],
numMessages: int.parse(data["numMessages"]),
numUnread: int.parse(data["unread"]),
@ -68,13 +69,7 @@ class CwtchNotifier {
}
if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"]) == null) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(data["ProfileOnion"], data["GroupID"],
authorization: ContactAuthorization.approved,
imagePath: data["PicturePath"],
nickname: data["GroupName"],
status: status,
server: data["GroupServer"],
isGroup: true,
lastMessageTime: DateTime.now()));
isInvitation: false, imagePath: data["PicturePath"], nickname: data["GroupName"], status: status, server: data["GroupServer"], isGroup: true, lastMessageTime: DateTime.now()));
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now());
}
break;
@ -96,7 +91,8 @@ class CwtchNotifier {
contact.status = data["ConnectionState"];
}
if (data["authorization"] != null) {
contact.authorization = stringToContactAuthorization(data["authorization"]);
contact.isInvitation = data["authorization"] == "unknown";
contact.isBlocked = data["authorization"] == "blocked";
}
// contact.[status/isBlocked] might change the list's sort order
profileCN.getProfile(data["ProfileOnion"])?.contactList.resort();
@ -149,18 +145,7 @@ class CwtchNotifier {
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.unreadMessages++;
}
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.totalMessages++;
var timestampSent = DateTime.tryParse(data['TimestampSent'])!;
// TODO: There are 2 timestamps associated with a new group message - time sent and time received.
// Sent refers to the time a profile alleges they sent a message
// Received refers to the time we actually saw the message from the server
// These can obviously be very different for legitimate reasons.
// We also maintain a relative hash-link through PreviousMessageSignature which is the ground truth for
// order.
// In the future we will want to combine these 3 ordering mechanisms into a cohesive view of the timeline
// For now we perform some minimal checks on the sent timestamp to use to provide a useful ordering for honest contacts
// and ensure that malicious contacts in groups can only set this timestamp to a value within the range of `last seen message time`
// and `local now`.
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], timestampSent.toLocal());
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now());
notificationManager.notify("New Message From Group!");
} else {
// from me (already displayed - do not update counter)
@ -192,6 +177,20 @@ class CwtchNotifier {
// ignore, we likely have an old key that has been replaced with an actual signature
}
break;
case "SendMessageToPeerError":
// from me (already displayed - do not update counter)
EnvironmentConfig.debugLog("SendMessageToPeerError");
var idx = data["EventID"];
var key = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.getMessageKey(idx);
if (key == null) break;
try {
var message = Provider.of<MessageMetadata>(key.currentContext!, listen: false);
if (message == null) break;
message.error = true;
} catch (e) {
// ignore, we likely have an old key that has been replaced with an actual signature
}
break;
case "SendMessageToGroupError":
// from me (already displayed - do not update counter)
EnvironmentConfig.debugLog("SendMessageToGroupError");
@ -256,22 +255,22 @@ class CwtchNotifier {
if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(groupInvite["GroupID"]) == null) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(data["ProfileOnion"], groupInvite["GroupID"],
authorization: ContactAuthorization.approved,
isInvitation: false,
imagePath: data["PicturePath"],
nickname: groupInvite["GroupName"],
server: groupInvite["ServerHost"],
status: status,
isGroup: true,
lastMessageTime: DateTime.fromMillisecondsSinceEpoch(0)));
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(groupInvite["GroupID"], DateTime.fromMillisecondsSinceEpoch(0));
lastMessageTime: DateTime.now()));
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(groupInvite["GroupID"], DateTime.now());
}
}
break;
case "AcceptGroupInvite":
EnvironmentConfig.debugLog("accept group invite");
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.authorization = ContactAuthorization.approved;
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.fromMillisecondsSinceEpoch(0));
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.isInvitation = false;
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now());
break;
case "ServerStateChange":
// Update the Server Cache

View File

@ -4,6 +4,7 @@ import 'dart:io';
import 'dart:isolate';
import 'dart:io' show Platform;
import 'package:cwtch/cwtch/cwtchNotifier.dart';
import 'package:flutter/src/services/text_input.dart';
import 'package:path/path.dart' as path;
import 'package:ffi/ffi.dart';
@ -21,9 +22,6 @@ typedef StartCwtchFn = int Function(Pointer<Utf8> dir, int len, Pointer<Utf8> to
typedef void_from_void_funtion = Void Function();
typedef VoidFromVoidFunction = void Function();
typedef free_function = Void Function(Pointer<Utf8>);
typedef FreeFn = void Function(Pointer<Utf8>);
typedef void_from_string_string_function = Void Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32);
typedef VoidFromStringStringFn = void Function(Pointer<Utf8>, int, Pointer<Utf8>, int);
@ -36,15 +34,25 @@ typedef VoidFromStringStringStringStringFn = void Function(Pointer<Utf8>, int, P
typedef void_from_string_string_int_int_function = Void Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Int64, Int64);
typedef VoidFromStringStringIntIntFn = void Function(Pointer<Utf8>, int, Pointer<Utf8>, int, int, int);
typedef access_cwtch_eventbus_function = Void Function();
typedef NextEventFn = void Function();
typedef string_to_void_function = Void Function(Pointer<Utf8> str, Int32 length);
typedef StringFn = void Function(Pointer<Utf8> dir, int);
typedef string_string_to_void_function = Void Function(Pointer<Utf8> str, Int32 length, Pointer<Utf8> str2, Int32 length2);
typedef StringStringFn = void Function(Pointer<Utf8>, int, Pointer<Utf8>, int);
typedef get_json_blob_void_function = Pointer<Utf8> Function();
typedef GetJsonBlobVoidFn = Pointer<Utf8> Function();
typedef get_json_blob_string_function = Pointer<Utf8> Function(Pointer<Utf8> str, Int32 length);
typedef GetJsonBlobStringFn = Pointer<Utf8> Function(Pointer<Utf8> str, int len);
//func NumMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int) (n C.int) {
typedef get_int_from_str_str_function = Int32 Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32);
typedef GetIntFromStrStrFn = int Function(Pointer<Utf8>, int, Pointer<Utf8>, int);
//func GetMessage(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, message_index C.int) *C.char {
typedef get_json_blob_from_str_str_int_function = Pointer<Utf8> Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Int32);
typedef GetJsonBlobFromStrStrIntFn = Pointer<Utf8> Function(Pointer<Utf8>, int, Pointer<Utf8>, int, int);
@ -130,7 +138,9 @@ class CwtchFfi implements Cwtch {
// Called on object being disposed to (presumably on app close) to close the isolate that's listening to libcwtch-go events
@override
void dispose() {
cwtchIsolate.kill(priority: Isolate.immediate);
if (cwtchIsolate != null) {
cwtchIsolate.kill(priority: Isolate.immediate);
}
}
// Entry point for an isolate to listen to a stream of events pulled from libcwtch-go and return them on the sendPort
@ -155,21 +165,9 @@ class CwtchFfi implements Cwtch {
// ignore: non_constant_identifier_names
final GetAppbusEvent = getAppbusEventC.asFunction<AppbusEventsFn>();
// Embedded Version of _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved
var free = library.lookup<NativeFunction<free_function>>("c_FreePointer");
final Free = free.asFunction<FreeFn>();
// ignore: non_constant_identifier_names
final GetAppBusEvent = () {
// ignore: non_constant_identifier_names
while (true) {
Pointer<Utf8> result = GetAppbusEvent();
String event = result.toDartString();
Free(result);
return event;
};
while (true) {
final event = GetAppBusEvent();
if (event.startsWith("{\"EventType\":\"Shutdown\"")) {
print("Shutting down isolate thread: $event");
@ -186,7 +184,6 @@ class CwtchFfi implements Cwtch {
final SelectProfile = selectProfileC.asFunction<GetJsonBlobStringFn>();
final ut8Onion = onion.toNativeUtf8();
SelectProfile(ut8Onion, ut8Onion.length);
malloc.free(ut8Onion);
}
// ignore: non_constant_identifier_names
@ -197,8 +194,6 @@ class CwtchFfi implements Cwtch {
final utf8nick = nick.toNativeUtf8();
final ut8pass = pass.toNativeUtf8();
CreateProfile(utf8nick, utf8nick.length, ut8pass, ut8pass.length);
malloc.free(utf8nick);
malloc.free(ut8pass);
}
// ignore: non_constant_identifier_names
@ -208,7 +203,6 @@ class CwtchFfi implements Cwtch {
final LoadProfiles = loadProfileC.asFunction<StringFn>();
final ut8pass = pass.toNativeUtf8();
LoadProfiles(ut8pass, ut8pass.length);
malloc.free(ut8pass);
}
// ignore: non_constant_identifier_names
@ -220,9 +214,6 @@ class CwtchFfi implements Cwtch {
final utf8handle = handle.toNativeUtf8();
Pointer<Utf8> jsonMessageBytes = GetMessage(utf8profile, utf8profile.length, utf8handle, utf8handle.length, index);
String jsonMessage = jsonMessageBytes.toDartString();
_UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes);
malloc.free(utf8profile);
malloc.free(utf8handle);
return jsonMessage;
}
@ -235,8 +226,6 @@ class CwtchFfi implements Cwtch {
final utf8onion = onion.toNativeUtf8();
final utf8json = json.toNativeUtf8();
SendAppBusEvent(utf8onion, utf8onion.length, utf8json, utf8json.length);
malloc.free(utf8onion);
malloc.free(utf8json);
}
@override
@ -247,7 +236,6 @@ class CwtchFfi implements Cwtch {
final SendAppBusEvent = sendAppBusEvent.asFunction<StringFn>();
final utf8json = json.toNativeUtf8();
SendAppBusEvent(utf8json, utf8json.length);
malloc.free(utf8json);
}
@override
@ -259,8 +247,6 @@ class CwtchFfi implements Cwtch {
final u1 = profileOnion.toNativeUtf8();
final u2 = contactHandle.toNativeUtf8();
AcceptContact(u1, u1.length, u2, u2.length);
malloc.free(u1);
malloc.free(u2);
}
@override
@ -272,8 +258,6 @@ class CwtchFfi implements Cwtch {
final u1 = profileOnion.toNativeUtf8();
final u2 = contactHandle.toNativeUtf8();
BlockContact(u1, u1.length, u2, u2.length);
malloc.free(u1);
malloc.free(u2);
}
@override
@ -286,9 +270,6 @@ class CwtchFfi implements Cwtch {
final u2 = contactHandle.toNativeUtf8();
final u3 = message.toNativeUtf8();
SendMessage(u1, u1.length, u2, u2.length, u3, u3.length);
malloc.free(u1);
malloc.free(u2);
malloc.free(u3);
}
@override
@ -301,9 +282,6 @@ class CwtchFfi implements Cwtch {
final u2 = contactHandle.toNativeUtf8();
final u3 = target.toNativeUtf8();
SendInvitation(u1, u1.length, u2, u2.length, u3, u3.length);
malloc.free(u1);
malloc.free(u2);
malloc.free(u3);
}
@override
@ -324,8 +302,6 @@ class CwtchFfi implements Cwtch {
final u1 = profileOnion.toNativeUtf8();
final u2 = bundle.toNativeUtf8();
ImportBundle(u1, u1.length, u2, u2.length);
malloc.free(u1);
malloc.free(u2);
}
@override
@ -339,10 +315,6 @@ class CwtchFfi implements Cwtch {
final u3 = key.toNativeUtf8();
final u4 = value.toNativeUtf8();
SetGroupAttribute(u1, u1.length, u2, u2.length, u3, u3.length, u4, u4.length);
malloc.free(u1);
malloc.free(u2);
malloc.free(u3);
malloc.free(u4);
}
@override
@ -354,12 +326,9 @@ class CwtchFfi implements Cwtch {
final u1 = profileOnion.toNativeUtf8();
final u2 = groupHandle.toNativeUtf8();
RejectInvite(u1, u1.length, u2, u2.length);
malloc.free(u1);
malloc.free(u2);
}
@override
// ignore: non_constant_identifier_names
void CreateGroup(String profileOnion, String server, String groupName) {
var createGroup = library.lookup<NativeFunction<void_from_string_string_string_function>>("c_CreateGroup");
// ignore: non_constant_identifier_names
@ -368,10 +337,6 @@ class CwtchFfi implements Cwtch {
final u2 = server.toNativeUtf8();
final u3 = groupName.toNativeUtf8();
CreateGroup(u1, u1.length, u2, u2.length, u3, u3.length);
malloc.free(u1);
malloc.free(u2);
malloc.free(u3);
}
@override
@ -383,8 +348,6 @@ class CwtchFfi implements Cwtch {
final u1 = profileOnion.toNativeUtf8();
final u2 = handle.toNativeUtf8();
LeaveConversation(u1, u1.length, u2, u2.length);
malloc.free(u1);
malloc.free(u2);
}
@override
@ -396,12 +359,9 @@ class CwtchFfi implements Cwtch {
final u1 = profileOnion.toNativeUtf8();
final u2 = groupHandle.toNativeUtf8();
LeaveGroup(u1, u1.length, u2, u2.length);
malloc.free(u1);
malloc.free(u2);
}
@override
// ignore: non_constant_identifier_names
void UpdateMessageFlags(String profile, String handle, int index, int flags) {
var updateMessageFlagsC = library.lookup<NativeFunction<void_from_string_string_int_int_function>>("c_UpdateMessageFlags");
// ignore: non_constant_identifier_names
@ -409,8 +369,6 @@ class CwtchFfi implements Cwtch {
final utf8profile = profile.toNativeUtf8();
final utf8handle = handle.toNativeUtf8();
updateMessageFlags(utf8profile, utf8profile.length, utf8handle, utf8handle.length, index, flags);
malloc.free(utf8profile);
malloc.free(utf8handle);
}
@override
@ -422,18 +380,14 @@ class CwtchFfi implements Cwtch {
final u1 = onion.toNativeUtf8();
final u2 = currentPassword.toNativeUtf8();
DeleteProfile(u1, u1.length, u2, u2.length);
malloc.free(u1);
malloc.free(u2);
}
@override
// ignore: non_constant_identifier_names
Future<void> Shutdown() async {
var shutdown = library.lookup<NativeFunction<void_from_void_funtion>>("c_ShutdownCwtch");
// ignore: non_constant_identifier_names
// Shutdown Cwtch + Tor...
// ignore: non_constant_identifier_names
final Shutdown = shutdown.asFunction<VoidFromVoidFunction>();
Shutdown();
@ -446,7 +400,6 @@ class CwtchFfi implements Cwtch {
}
@override
// ignore: non_constant_identifier_names
Future GetMessageByContentHash(String profile, String handle, String contentHash) async {
var getMessagesByContentHashC = library.lookup<NativeFunction<get_json_blob_from_str_str_str_function>>("c_GetMessagesByContentHash");
// ignore: non_constant_identifier_names
@ -456,20 +409,6 @@ class CwtchFfi implements Cwtch {
final utf8contentHash = contentHash.toNativeUtf8();
Pointer<Utf8> jsonMessageBytes = GetMessagesByContentHash(utf8profile, utf8profile.length, utf8handle, utf8handle.length, utf8contentHash, utf8contentHash.length);
String jsonMessage = jsonMessageBytes.toDartString();
_UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes);
malloc.free(utf8profile);
malloc.free(utf8handle);
malloc.free(utf8contentHash);
return jsonMessage;
}
// ignore: non_constant_identifier_names
// Incredibly dangerous function which invokes a free in libCwtch, should only be used
// as documented in `MEMORY.md` in libCwtch repo.
void _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(Pointer<Utf8> ptr) {
var free = library.lookup<NativeFunction<free_function>>("c_FreePointer");
final Free = free.asFunction<FreeFn>();
Free(ptr);
}
}

View File

@ -1,28 +1,6 @@
{
"@@locale": "de",
"@@last_modified": "2021-08-26T23:44:51+02:00",
"profileOnionLabel": "Senden Sie diese Adresse an Peers, mit denen Sie sich verbinden möchten",
"addPeerTab": "Einen anderen Nutzer hinzufügen",
"addPeer": "Anderen Nutzer hinzufügen",
"peerNotOnline": "Der andere Nutzer ist offline. Die App kann momentan nicht verwendet werden.",
"peerBlockedMessage": "Anderer Nutzer ist blockiert",
"peerOfflineMessage": "Anderer Nutzer ist offline, Nachrichten können derzeit nicht zugestellt werden",
"blockBtn": "Anderen Nutzer blockieren",
"savePeerHistory": "Peer-Verlauf speichern",
"savePeerHistoryDescription": "Legt fest, ob ein mit dem anderen Nutzer verknüpfter Verlauf gelöscht werden soll oder nicht.",
"dontSavePeerHistory": "Verlauf mit anderem Nutzer löschen",
"unblockBtn": "Anderen Nutzer entsperren",
"blockUnknownLabel": "Unbekannte Peers blockieren",
"blockUnknownConnectionsEnabledDescription": "Connections from unknown contacts are blocked. You can change this in Settings",
"networkStatusConnecting": "Verbinde zu Netzwerk und Peers ...",
"showMessageButton": "Show Message",
"blockedMessageMessage": "This message is from a profile you have blocked.",
"placeholderEnterMessage": "Type a message...",
"plainProfileDescription": "We recommend that you protect your Cwtch profiles with a password. If you do not set a password on this profile then anyone who has access to this device may be able to access information about this profile, including contacts, messages and sensitive cryptographic keys.",
"encryptedProfileDescription": "Encrypting a profile with a password protects it from other people who may also use this device. Encrypted profiles cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.",
"addContactConfirm": "Add contact %1",
"addContact": "Add contact",
"contactGoto": "Go to conversation with %1",
"@@last_modified": "2021-07-07T23:42:20+02:00",
"settingUIColumnOptionSame": "Same as portrait mode setting",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",
@ -98,6 +76,7 @@
"todoPlaceholder": "noch zu erledigen",
"newConnectionPaneTitle": "Neue Verbindung",
"networkStatusOnline": "Online",
"networkStatusConnecting": "Verbinde zu Netzwerk und Peers ...",
"networkStatusAttemptingTor": "Versuche, eine Verbindung mit dem Tor-Netzwerk herzustellen",
"networkStatusDisconnected": "Vom Internet getrennt, überprüfe deine Verbindung",
"viewGroupMembershipTooltip": "Gruppenmitgliedschaft anzeigen",
@ -117,6 +96,7 @@
"localeFr": "Frances",
"localeEn": "English",
"settingLanguage": "Sprache",
"blockUnknownLabel": "Unbekannte Peers blockieren",
"zoomLabel": "Benutzeroberflächen-Zoom (betriftt hauptsächlich Text- und Knopgrößen)",
"versionBuilddate": "Version: %1 Aufgebaut auf: %2",
"cwtchSettingsTitle": "Cwtch Einstellungen",
@ -140,6 +120,7 @@
"password1Label": "Passwort",
"currentPasswordLabel": "aktuelles Passwort",
"yourDisplayName": "Dein Anzeigename",
"profileOnionLabel": "Sende diese Adresse an andere Nutzer, mit denen du dich verbinden möchtest",
"noPasswordWarning": "Wenn für dieses Konto kein Passwort verwendet wird, bedeutet dies, dass alle lokal gespeicherten Daten nicht verschlüsselt werden.",
"radioNoPassword": "Unverschlüsselt (kein Passwort)",
"radioUsePassword": "Passwort",
@ -151,8 +132,13 @@
"profileName": "Anzeigename",
"editProfileTitle": "Profil bearbeiten",
"addProfileTitle": "Neues Profil hinzufügen",
"deleteBtn": "Löschen",
"saveBtn": "Speichern",
"deleteBtn": "löschen",
"unblockBtn": "Anderen Nutzer entsperren",
"dontSavePeerHistory": "Verlauf mit anderem Nutzer löschen",
"savePeerHistoryDescription": "Legt fest, ob ein mit dem anderen Nutzer verknüpfter Verlauf gelöscht werden soll oder nicht.",
"savePeerHistory": "Peer-Verlauf speichern",
"blockBtn": "Anderen Nutzer blockieren",
"saveBtn": "speichern",
"displayNameLabel": "Angezeigename",
"addressLabel": "Adresse",
"puzzleGameBtn": "Puzzlespiel",
@ -164,12 +150,15 @@
"acceptGroupInviteLabel": "Möchtest Du die Einladung annehmen",
"newGroupBtn": "Neue Gruppe anlegen",
"copiedClipboardNotification": "in die Zwischenablage kopiert",
"peerOfflineMessage": "Anderer Nutzer ist offline, Nachrichten können derzeit nicht zugestellt werden",
"peerBlockedMessage": "Anderer Nutzer ist blockiert",
"pendingLabel": "Bestätigung ausstehend",
"acknowledgedLabel": "bestätigt",
"couldNotSendMsgError": "Nachricht konnte nicht gesendet werden",
"dmTooltip": "Klicken, um Direktnachricht zu senden",
"membershipDescription": "Unten steht eine Liste der Benutzer, die Nachrichten an die Gruppe gesendet haben. Möglicherweise enthält diese Benutzerzliste nicht alle, die Zugang zur Gruppe haben.",
"addListItemBtn": "Element hinzufügen",
"peerNotOnline": "Der andere Nutzer ist offline. Die App kann momentan nicht verwendet werden.",
"searchList": "Liste durchsuchen",
"update": "Update",
"inviteBtn": "Einladen",
@ -195,6 +184,7 @@
"newBulletinLabel": "Neue Meldung",
"joinGroup": "Gruppe beitreten",
"createGroup": "Gruppe erstellen",
"addPeer": "Anderen Nutzer hinzufügen",
"groupAddr": "Adresse",
"invitation": "Einladung",
"server": "Server",
@ -203,6 +193,7 @@
"peerAddress": "Adresse",
"joinGroupTab": "Einer Gruppe beitreten",
"createGroupTab": "Eine Gruppe erstellen",
"addPeerTab": "Einen anderen Nutzer hinzufügen",
"createGroupBtn": "Anlegen",
"defaultGroupName": "Tolle Gruppe",
"createGroupTitle": "Gruppe Anlegen"

View File

@ -1,28 +1,6 @@
{
"@@locale": "en",
"@@last_modified": "2021-08-26T23:44:51+02:00",
"profileOnionLabel": "Send this address to people you want to connect with",
"addPeerTab": "Add a contact",
"addPeer": "Add Contact",
"peerNotOnline": "Contact is offline. Applications cannot be used right now.",
"peerBlockedMessage": "Contact is blocked",
"peerOfflineMessage": "Contact is offline, messages can't be delivered right now",
"blockBtn": "Block Contact",
"savePeerHistory": "Save History",
"savePeerHistoryDescription": "Determines whether or not to delete any history associated with the contact.",
"dontSavePeerHistory": "Delete History",
"unblockBtn": "Unblock Contact",
"blockUnknownLabel": "Block Unknown Contacts",
"blockUnknownConnectionsEnabledDescription": "Connections from unknown contacts are blocked. You can change this in Settings",
"networkStatusConnecting": "Connecting to network and contacts...",
"showMessageButton": "Show Message",
"blockedMessageMessage": "This message is from a profile you have blocked.",
"placeholderEnterMessage": "Type a message...",
"plainProfileDescription": "We recommend that you protect your Cwtch profiles with a password. If you do not set a password on this profile then anyone who has access to this device may be able to access information about this profile, including contacts, messages and sensitive cryptographic keys.",
"encryptedProfileDescription": "Encrypting a profile with a password protects it from other people who may also use this device. Encrypted profiles cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.",
"addContactConfirm": "Add contact %1",
"addContact": "Add contact",
"contactGoto": "Go to conversation with %1",
"@@last_modified": "2021-07-07T23:42:20+02:00",
"settingUIColumnOptionSame": "Same as portrait mode setting",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",
@ -98,6 +76,7 @@
"todoPlaceholder": "Todo...",
"newConnectionPaneTitle": "New Connection",
"networkStatusOnline": "Online",
"networkStatusConnecting": "Connecting to network and peers...",
"networkStatusAttemptingTor": "Attempting to connect to Tor network",
"networkStatusDisconnected": "Disconnected from the internet, check your connection",
"viewGroupMembershipTooltip": "View Group Membership",
@ -117,6 +96,7 @@
"localeFr": "Frances",
"localeEn": "English",
"settingLanguage": "Language",
"blockUnknownLabel": "Block Unknown Peers",
"zoomLabel": "Interface zoom (mostly affects text and button sizes)",
"versionBuilddate": "Version: %1 Built on: %2",
"cwtchSettingsTitle": "Cwtch Settings",
@ -140,6 +120,7 @@
"password1Label": "Password",
"currentPasswordLabel": "Current Password",
"yourDisplayName": "Your Display Name",
"profileOnionLabel": "Send this address to peers you want to connect with",
"noPasswordWarning": "Not using a password on this account means that all data stored locally will not be encrypted",
"radioNoPassword": "Unencrypted (No password)",
"radioUsePassword": "Password",
@ -152,6 +133,11 @@
"editProfileTitle": "Edit Profile",
"addProfileTitle": "Add new profile",
"deleteBtn": "Delete",
"unblockBtn": "Unblock Peer",
"dontSavePeerHistory": "Delete Peer History",
"savePeerHistoryDescription": "Determines whether or not to delete any history associated with the peer.",
"savePeerHistory": "Save Peer History",
"blockBtn": "Block Peer",
"saveBtn": "Save",
"displayNameLabel": "Display Name",
"addressLabel": "Address",
@ -164,17 +150,20 @@
"acceptGroupInviteLabel": "Do you want to accept the invitation to",
"newGroupBtn": "Create new group",
"copiedClipboardNotification": "Copied to clipboard",
"peerOfflineMessage": "Peer is offline, messages can't be delivered right now",
"peerBlockedMessage": "Peer is blocked",
"pendingLabel": "Pending",
"acknowledgedLabel": "Acknowledged",
"couldNotSendMsgError": "Could not send this message",
"dmTooltip": "Click to DM",
"membershipDescription": "Below is a list of users who have sent messages to the group. This list may not reflect all users who have access to the group.",
"addListItemBtn": "Add Item",
"peerNotOnline": "Peer is Offline. Applications cannot be used right now.",
"searchList": "Search List",
"update": "Update",
"inviteBtn": "Invite",
"inviteToGroupLabel": "Invite to group",
"groupNameLabel": "Group name",
"groupNameLabel": "Group Name",
"viewServerInfo": "Server Info",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server Disconnected",
@ -195,6 +184,7 @@
"newBulletinLabel": "New Bulletin",
"joinGroup": "Join group",
"createGroup": "Create group",
"addPeer": "Add Peer",
"groupAddr": "Address",
"invitation": "Invitation",
"server": "Server",
@ -203,6 +193,7 @@
"peerAddress": "Address",
"joinGroupTab": "Join a group",
"createGroupTab": "Create a group",
"addPeerTab": "Add a peer",
"createGroupBtn": "Create",
"defaultGroupName": "Awesome Group",
"createGroupTitle": "Create Group"

View File

@ -1,28 +1,6 @@
{
"@@locale": "es",
"@@last_modified": "2021-08-26T23:44:51+02:00",
"profileOnionLabel": "Envía esta dirección a los contactos con los que quieras conectarte",
"addPeerTab": "Agregar Contacto",
"addPeer": "Agregar Contacto",
"peerNotOnline": "Este contacto no está en línea, la aplicación no puede ser usada en este momento",
"peerBlockedMessage": "Contacto bloqueado",
"peerOfflineMessage": "Este contacto no está en línea, los mensajes no pueden ser entregados en este momento",
"blockBtn": "Bloquear contacto",
"savePeerHistory": "Guardar el historial con contacto",
"savePeerHistoryDescription": "Determina si eliminar o no el historial asociado con el contacto.",
"dontSavePeerHistory": "Eliminar historial de contacto",
"unblockBtn": "Desbloquear contacto",
"blockUnknownLabel": "Bloquear conexiones desconocidas",
"blockUnknownConnectionsEnabledDescription": "Connections from unknown contacts are blocked. You can change this in Settings",
"networkStatusConnecting": "Conectando a la red y a los contactos...",
"showMessageButton": "Show Message",
"blockedMessageMessage": "This message is from a profile you have blocked.",
"placeholderEnterMessage": "Type a message...",
"plainProfileDescription": "We recommend that you protect your Cwtch profiles with a password. If you do not set a password on this profile then anyone who has access to this device may be able to access information about this profile, including contacts, messages and sensitive cryptographic keys.",
"encryptedProfileDescription": "Encrypting a profile with a password protects it from other people who may also use this device. Encrypted profiles cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.",
"addContactConfirm": "Add contact %1",
"addContact": "Add contact",
"contactGoto": "Go to conversation with %1",
"@@last_modified": "2021-07-07T23:42:20+02:00",
"settingUIColumnOptionSame": "Same as portrait mode setting",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",
@ -98,6 +76,7 @@
"todoPlaceholder": "Por hacer...",
"newConnectionPaneTitle": "Nueva conexión",
"networkStatusOnline": "En línea",
"networkStatusConnecting": "Conectando a la red y a los contactos...",
"networkStatusAttemptingTor": "Intentando conectarse a la red Tor",
"networkStatusDisconnected": "Sin conexión, comprueba tu conexión",
"viewGroupMembershipTooltip": "Ver membresía del grupo",
@ -117,6 +96,7 @@
"localeFr": "Francés",
"localeEn": "Inglés",
"settingLanguage": "Idioma",
"blockUnknownLabel": "Bloquear conexiones desconocidas",
"zoomLabel": "Zoom de la interfaz (afecta principalmente el tamaño del texto y de los botones)",
"versionBuilddate": "Versión: %1 Basado en %2",
"cwtchSettingsTitle": "Configuración de Cwtch",
@ -140,6 +120,7 @@
"password1Label": "Contraseña",
"currentPasswordLabel": "Contraseña actual",
"yourDisplayName": "Tu nombre de usuario",
"profileOnionLabel": "Envía esta dirección a los contactos con los que quieras conectarte",
"noPasswordWarning": "No usar una contraseña para esta cuenta significa que los datos almacenados localmente no serán encriptados",
"radioNoPassword": "Sin cifrado (sin contraseña)",
"radioUsePassword": "Contraseña",
@ -152,6 +133,11 @@
"editProfileTitle": "Editar perfil",
"addProfileTitle": "Agregar nuevo perfil",
"deleteBtn": "Eliminar",
"unblockBtn": "Desbloquear contacto",
"dontSavePeerHistory": "Eliminar historial de contacto",
"savePeerHistoryDescription": "Determina si eliminar o no el historial asociado con el contacto.",
"savePeerHistory": "Guardar el historial con contacto",
"blockBtn": "Bloquear contacto",
"saveBtn": "Guardar",
"displayNameLabel": "Nombre de Usuario",
"addressLabel": "Dirección",
@ -164,12 +150,15 @@
"acceptGroupInviteLabel": "¿Quieres aceptar la invitación a ",
"newGroupBtn": "Crear un nuevo grupo de chat",
"copiedClipboardNotification": "Copiado al portapapeles",
"peerOfflineMessage": "Este contacto no está en línea, los mensajes no pueden ser entregados en este momento",
"peerBlockedMessage": "Contacto bloqueado",
"pendingLabel": "Pendiente",
"acknowledgedLabel": "Reconocido",
"couldNotSendMsgError": "No se pudo enviar este mensaje",
"dmTooltip": "Haz clic para enviar mensaje directo",
"membershipDescription": "La lista a continuación solo muestra los miembros que han enviado mensajes al grupo, no incluye a todos los usuarios dentro del grupo",
"addListItemBtn": "Agregar artículo",
"peerNotOnline": "Este contacto no está en línea, la aplicación no puede ser usada en este momento",
"searchList": "Buscar en la lista",
"update": "Actualizar",
"inviteBtn": "Invitar",
@ -195,6 +184,7 @@
"newBulletinLabel": "Nuevo Boletín",
"joinGroup": "Únete al grupo",
"createGroup": "Crear perfil",
"addPeer": "Agregar Contacto",
"groupAddr": "Dirección",
"invitation": "Invitación",
"server": "Servidor",
@ -203,6 +193,7 @@
"peerAddress": "Dirección",
"joinGroupTab": "Únete a un grupo",
"createGroupTab": "Crear un grupo",
"addPeerTab": "Agregar Contacto",
"createGroupBtn": "Crear",
"defaultGroupName": "El Grupo Asombroso",
"createGroupTitle": "Crear un grupo"

View File

@ -1,28 +1,6 @@
{
"@@locale": "fr",
"@@last_modified": "2021-08-26T23:44:51+02:00",
"profileOnionLabel": "Envoyez cette adresse aux personnes avec lesquelles vous souhaitez entrer en contact.",
"addPeerTab": "Ajouter un pair",
"addPeer": "Ajouter un pair",
"peerNotOnline": "Le pair est hors ligne, les messages ne peuvent pas être remis pour le moment",
"peerBlockedMessage": "Le pair est bloqué",
"peerOfflineMessage": "Le pair est hors ligne, les messages ne peuvent pas être remis pour le moment",
"blockBtn": "Bloquer le pair",
"savePeerHistory": "Sauvegarder l'historique des pairs",
"savePeerHistoryDescription": "Détermine s'il faut ou non supprimer tout historique associé au pair.",
"dontSavePeerHistory": "Supprimer l'historique des pairs",
"unblockBtn": "Débloquer le pair",
"blockUnknownLabel": "Bloquer les pairs inconnus",
"blockUnknownConnectionsEnabledDescription": "Connections from unknown contacts are blocked. You can change this in Settings",
"networkStatusConnecting": "Se connecter au réseau et aux pairs...",
"showMessageButton": "Afficher le message",
"blockedMessageMessage": "Ce message provient d'un profil que vous avez bloqué.",
"placeholderEnterMessage": "saisissez un message",
"plainProfileDescription": "Nous vous recommandons de protéger vos profils Cwtch par un mot de passe. Si vous ne définissez pas de mot de passe sur ce profil, toute personne ayant accès à cet appareil peut être en mesure d'accéder aux informations relatives à ce profil, y compris les contacts, les messages et les clés de chiffrement sensibles.",
"encryptedProfileDescription": "Le chiffrement d'un profil à l'aide d'un mot de passe le protège des autres personnes susceptibles d'utiliser également cet appareil. Les profils chiffrés ne peuvent pas être déchiffrés , affichés ou accessibles tant que le mot de passe correct n'a pas été saisi pour les déverrouiller.",
"addContactConfirm": "Ajouter le contact %1",
"addContact": "Ajouter le contact",
"contactGoto": "Aller à la conversation avec %1",
"@@last_modified": "2021-07-07T23:42:20+02:00",
"settingUIColumnOptionSame": "Même réglage que pour le mode portrait",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",
@ -98,6 +76,7 @@
"todoPlaceholder": "À faire...",
"newConnectionPaneTitle": "Nouvelle connexion",
"networkStatusOnline": "En ligne",
"networkStatusConnecting": "Se connecter au réseau et aux pairs...",
"networkStatusAttemptingTor": "Tentative de connexion au réseau Tor",
"networkStatusDisconnected": "Déconnecté d'Internet, vérifiez votre connexion",
"viewGroupMembershipTooltip": "Afficher les membres du groupe",
@ -117,6 +96,7 @@
"localeFr": "Français",
"localeEn": "Anglais",
"settingLanguage": "Langue",
"blockUnknownLabel": "Bloquer les pairs inconnus",
"zoomLabel": "Zoom de l'interface (affecte principalement la taille du texte et des boutons)",
"versionBuilddate": "Version : %1 Construite le : %2",
"cwtchSettingsTitle": "Préférences Cwtch",
@ -140,6 +120,7 @@
"password1Label": "Mot de passe",
"currentPasswordLabel": "Mot de passe actuel",
"yourDisplayName": "Pseudo",
"profileOnionLabel": "Envoyez cette adresse aux personnes avec lesquelles vous souhaitez entrer en contact.",
"noPasswordWarning": "Ne pas utiliser de mot de passe sur ce compte signifie que toutes les données stockées localement ne seront pas chiffrées.",
"radioNoPassword": "Non chiffré (pas de mot de passe)",
"radioUsePassword": "Mot de passe",
@ -151,7 +132,12 @@
"profileName": "Pseudo",
"editProfileTitle": "Modifier le profil",
"addProfileTitle": "Ajouter un nouveau profil",
"deleteBtn": "Effacer",
"deleteBtn": "Supprimer",
"unblockBtn": "Débloquer le pair",
"dontSavePeerHistory": "Supprimer l'historique des pairs",
"savePeerHistoryDescription": "Détermine s'il faut ou non supprimer tout historique associé au pair.",
"savePeerHistory": "Sauvegarder l'historique des pairs",
"blockBtn": "Bloquer le pair",
"saveBtn": "Sauvegarder",
"displayNameLabel": "Pseudo",
"addressLabel": "Adresse",
@ -164,12 +150,15 @@
"acceptGroupInviteLabel": "Voulez-vous accepter l'invitation au groupe",
"newGroupBtn": "Créer un nouveau groupe",
"copiedClipboardNotification": "Copié dans le presse-papier",
"peerOfflineMessage": "Le pair est hors ligne, les messages ne peuvent pas être remis pour le moment",
"peerBlockedMessage": "Le pair est bloqué",
"pendingLabel": "En attente",
"acknowledgedLabel": "Accusé de réception",
"couldNotSendMsgError": "Impossible d'envoyer ce message",
"dmTooltip": "Envoyer un message privé",
"membershipDescription": "Liste des utilisateurs ayant envoyés un ou plusieurs messages au groupe. Cette liste peut ne pas être représentatives de l'ensemble des membres du groupe.",
"addListItemBtn": "Ajouter un élément",
"peerNotOnline": "Le pair est hors ligne, les messages ne peuvent pas être remis pour le moment",
"searchList": "Liste de recherche",
"update": "Mise à jour",
"inviteBtn": "Invitation",
@ -195,6 +184,7 @@
"newBulletinLabel": "Nouveau bulletin",
"joinGroup": "Rejoindre le groupe",
"createGroup": "Créer un groupe",
"addPeer": "Ajouter un pair",
"groupAddr": "Adresse",
"invitation": "Invitation",
"server": "Serveur",
@ -203,6 +193,7 @@
"peerAddress": "Adresse",
"joinGroupTab": "Rejoindre un groupe",
"createGroupTab": "Créer un groupe",
"addPeerTab": "Ajouter un pair",
"createGroupBtn": "Créer",
"defaultGroupName": "Un groupe génial",
"createGroupTitle": "Créer un groupe"

View File

@ -1,28 +1,6 @@
{
"@@locale": "it",
"@@last_modified": "2021-08-26T23:44:51+02:00",
"profileOnionLabel": "Inviare questo indirizzo ai peer con cui si desidera connettersi",
"addPeerTab": "Aggiungi un peer",
"addPeer": "Aggiungi peer",
"peerNotOnline": "Il peer è offline. Le applicazioni non possono essere utilizzate in questo momento.",
"peerBlockedMessage": "Il peer è bloccato",
"peerOfflineMessage": "Il peer è offline, i messaggi non possono essere recapitati in questo momento",
"blockBtn": "Blocca il peer",
"savePeerHistory": "Salva cronologia peer",
"savePeerHistoryDescription": "Determina se eliminare o meno ogni cronologia eventualmente associata al peer.",
"dontSavePeerHistory": "Elimina cronologia dei peer",
"unblockBtn": "Sblocca il peer",
"blockUnknownLabel": "Blocca peer sconosciuti",
"blockUnknownConnectionsEnabledDescription": "Connections from unknown contacts are blocked. You can change this in Settings",
"networkStatusConnecting": "Connessione alla rete e ai peer ...",
"showMessageButton": "Show Message",
"blockedMessageMessage": "This message is from a profile you have blocked.",
"placeholderEnterMessage": "Type a message...",
"plainProfileDescription": "We recommend that you protect your Cwtch profiles with a password. If you do not set a password on this profile then anyone who has access to this device may be able to access information about this profile, including contacts, messages and sensitive cryptographic keys.",
"encryptedProfileDescription": "Encrypting a profile with a password protects it from other people who may also use this device. Encrypted profiles cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.",
"addContactConfirm": "Add contact %1",
"addContact": "Add contact",
"contactGoto": "Go to conversation with %1",
"@@last_modified": "2021-07-07T23:42:20+02:00",
"settingUIColumnOptionSame": "Same as portrait mode setting",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",
@ -98,6 +76,7 @@
"todoPlaceholder": "Da fare...",
"newConnectionPaneTitle": "Nuova connessione",
"networkStatusOnline": "Online",
"networkStatusConnecting": "Connessione alla rete e ai peer ...",
"networkStatusAttemptingTor": "Tentativo di connessione alla rete Tor",
"networkStatusDisconnected": "Disconnesso da Internet, controlla la tua connessione",
"viewGroupMembershipTooltip": "Visualizza i membri del gruppo",
@ -117,6 +96,7 @@
"localeFr": "Francese",
"localeEn": "Inglese",
"settingLanguage": "Lingua",
"blockUnknownLabel": "Blocca peer sconosciuti",
"zoomLabel": "Zoom dell'interfaccia (influisce principalmente sulle dimensioni del testo e dei pulsanti)",
"versionBuilddate": "Versione: %1 Costruito il: %2",
"cwtchSettingsTitle": "Impostazioni di Cwtch",
@ -140,10 +120,11 @@
"password1Label": "Password",
"currentPasswordLabel": "Password corrente",
"yourDisplayName": "Il tuo nome visualizzato",
"profileOnionLabel": "Inviare questo indirizzo ai peer con cui si desidera connettersi",
"noPasswordWarning": "Non utilizzare una password su questo account significa che tutti i dati archiviati localmente non verranno criptati",
"radioNoPassword": "Non criptato (senza password)",
"radioUsePassword": "Password",
"copiedToClipboardNotification": "Copiato negli Appunti",
"copiedToClipboardNotification": "Copiato negli appunti",
"copyBtn": "Copia",
"editProfile": "Modifica profilo",
"newProfile": "Nuovo profilo",
@ -152,6 +133,11 @@
"editProfileTitle": "Modifica profilo",
"addProfileTitle": "Aggiungi nuovo profilo",
"deleteBtn": "Elimina",
"unblockBtn": "Sblocca il peer",
"dontSavePeerHistory": "Elimina cronologia dei peer",
"savePeerHistoryDescription": "Determina se eliminare o meno ogni cronologia eventualmente associata al peer.",
"savePeerHistory": "Salva cronologia peer",
"blockBtn": "Blocca il peer",
"saveBtn": "Salva",
"displayNameLabel": "Nome visualizzato",
"addressLabel": "Indirizzo",
@ -164,12 +150,15 @@
"acceptGroupInviteLabel": "Vuoi accettare l'invito a",
"newGroupBtn": "Crea un nuovo gruppo",
"copiedClipboardNotification": "Copiato negli Appunti",
"peerOfflineMessage": "Il peer è offline, i messaggi non possono essere recapitati in questo momento",
"peerBlockedMessage": "Il peer è bloccato",
"pendingLabel": "In corso",
"acknowledgedLabel": "Riconosciuto",
"couldNotSendMsgError": "Impossibile inviare questo messaggio",
"dmTooltip": "Clicca per inviare un Messagio Diretto",
"membershipDescription": "Di seguito è riportato un elenco di utenti che hanno inviato messaggi al gruppo. Questo elenco potrebbe non corrispondere a tutti gli utenti che hanno accesso al gruppo.",
"addListItemBtn": "Aggiungi elemento",
"peerNotOnline": "Il peer è offline. Le applicazioni non possono essere utilizzate in questo momento.",
"searchList": "Cerca nella lista",
"update": "Aggiornamento",
"inviteBtn": "Invitare",
@ -195,6 +184,7 @@
"newBulletinLabel": "Nuovo bollettino",
"joinGroup": "Unisciti al gruppo",
"createGroup": "Crea un gruppo",
"addPeer": "Aggiungi peer",
"groupAddr": "Indirizzo",
"invitation": "Invito",
"server": "Server",
@ -203,6 +193,7 @@
"peerAddress": "Indirizzo",
"joinGroupTab": "Unisciti a un gruppo",
"createGroupTab": "Crea un gruppo",
"addPeerTab": "Aggiungi un peer",
"createGroupBtn": "Crea",
"defaultGroupName": "Gruppo fantastico",
"createGroupTitle": "Crea un gruppo"

View File

@ -1,14 +1,6 @@
{
"@@locale": "pl",
"@@last_modified": "2021-07-14T23:49:07+02:00",
"showMessageButton": "Show Message",
"blockedMessageMessage": "This message is from a profile you have blocked.",
"placeholderEnterMessage": "Type a message...",
"plainProfileDescription": "We recommend that you protect your Cwtch profiles with a password. If you do not set a password on this profile then anyone who has access to this device may be able to access information about this profile, including contacts, messages and sensitive cryptographic keys.",
"encryptedProfileDescription": "Encrypting a profile with a password protects it from other people who may also use this device. Encrypted profiles cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.",
"addContactConfirm": "Add contact %1",
"addContact": "Add contact",
"contactGoto": "Go to conversation with %1",
"@@last_modified": "2021-07-07T23:42:20+02:00",
"settingUIColumnOptionSame": "Same as portrait mode setting",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",
@ -171,7 +163,7 @@
"update": "Update",
"inviteBtn": "Invite",
"inviteToGroupLabel": "Invite to group",
"groupNameLabel": "Group name",
"groupNameLabel": "Group Name",
"viewServerInfo": "Server Info",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server Disconnected",

View File

@ -1,28 +1,6 @@
{
"@@locale": "pt",
"@@last_modified": "2021-08-26T23:44:51+02:00",
"profileOnionLabel": "Send this address to contacts you want to connect with",
"addPeerTab": "Add a contact",
"addPeer": "Add Contact",
"peerNotOnline": "Contact is offline. Applications cannot be used right now.",
"peerBlockedMessage": "Contact is blocked",
"peerOfflineMessage": "Contact is offline, messages can't be delivered right now",
"blockBtn": "Block Contact",
"savePeerHistory": "Save History",
"savePeerHistoryDescription": "Determines whether or not to delete any history associated with the contact.",
"dontSavePeerHistory": "Delete History",
"unblockBtn": "Unblock Contact",
"blockUnknownLabel": "Block Unknown Contacts",
"blockUnknownConnectionsEnabledDescription": "Connections from unknown contacts are blocked. You can change this in Settings",
"networkStatusConnecting": "Connecting to network and contacts...",
"showMessageButton": "Show Message",
"blockedMessageMessage": "This message is from a profile you have blocked.",
"placeholderEnterMessage": "Type a message...",
"plainProfileDescription": "We recommend that you protect your Cwtch profiles with a password. If you do not set a password on this profile then anyone who has access to this device may be able to access information about this profile, including contacts, messages and sensitive cryptographic keys.",
"encryptedProfileDescription": "Encrypting a profile with a password protects it from other people who may also use this device. Encrypted profiles cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.",
"addContactConfirm": "Add contact %1",
"addContact": "Add contact",
"contactGoto": "Go to conversation with %1",
"@@last_modified": "2021-07-07T23:42:20+02:00",
"settingUIColumnOptionSame": "Same as portrait mode setting",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",
@ -98,6 +76,7 @@
"todoPlaceholder": "Afazer…",
"newConnectionPaneTitle": "New Connection",
"networkStatusOnline": "Online",
"networkStatusConnecting": "Connecting to network and peers...",
"networkStatusAttemptingTor": "Attempting to connect to Tor network",
"networkStatusDisconnected": "Disconnected from the internet, check your connection",
"viewGroupMembershipTooltip": "View Group Membership",
@ -117,6 +96,7 @@
"localeFr": "Frances",
"localeEn": "English",
"settingLanguage": "Language",
"blockUnknownLabel": "Block Unknown Peers",
"zoomLabel": "Zoom da interface (afeta principalmente tamanho de texto e botões)",
"versionBuilddate": "Version: %1 Built on: %2",
"cwtchSettingsTitle": "Configurações do Cwtch",
@ -140,6 +120,7 @@
"password1Label": "Password",
"currentPasswordLabel": "Current Password",
"yourDisplayName": "Your Display Name",
"profileOnionLabel": "Send this address to peers you want to connect with",
"noPasswordWarning": "Not using a password on this account means that all data stored locally will not be encrypted",
"radioNoPassword": "Unencrypted (No password)",
"radioUsePassword": "Password",
@ -152,6 +133,11 @@
"editProfileTitle": "Edit Profile",
"addProfileTitle": "Add new profile",
"deleteBtn": "Deletar",
"unblockBtn": "Unblock Peer",
"dontSavePeerHistory": "Delete Peer History",
"savePeerHistoryDescription": "Determines whether or not to delete any history associated with the peer.",
"savePeerHistory": "Save Peer History",
"blockBtn": "Block Peer",
"saveBtn": "Salvar",
"displayNameLabel": "Nome de Exibição",
"addressLabel": "Endereço",
@ -164,17 +150,20 @@
"acceptGroupInviteLabel": "Você quer aceitar o convite para",
"newGroupBtn": "Criar novo grupo",
"copiedClipboardNotification": "Copiado",
"peerOfflineMessage": "Peer is offline, messages can't be delivered right now",
"peerBlockedMessage": "Peer is blocked",
"pendingLabel": "Pendente",
"acknowledgedLabel": "Confirmada",
"couldNotSendMsgError": "Não deu para enviar esta mensagem",
"dmTooltip": "Clique para DM",
"membershipDescription": "A lista abaixo é de usuários que enviaram mensagens ao grupo. Essa lista pode não refletir todos os usuários que têm acesso ao grupo.",
"addListItemBtn": "Add Item",
"peerNotOnline": "Peer is Offline. Applications cannot be used right now.",
"searchList": "Search List",
"update": "Update",
"inviteBtn": "Convidar",
"inviteToGroupLabel": "Convidar ao grupo",
"groupNameLabel": "Nome do grupo",
"groupNameLabel": "Nome do Grupo",
"viewServerInfo": "Server Info",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server Disconnected",
@ -195,6 +184,7 @@
"newBulletinLabel": "Novo Boletim",
"joinGroup": "Join group",
"createGroup": "Create group",
"addPeer": "Add Peer",
"groupAddr": "Address",
"invitation": "Invitation",
"server": "Server",
@ -203,6 +193,7 @@
"peerAddress": "Address",
"joinGroupTab": "Join a group",
"createGroupTab": "Create a group",
"addPeerTab": "Add a peer",
"createGroupBtn": "Criar",
"defaultGroupName": "Grupo incrível",
"createGroupTitle": "Criar Grupo"

View File

@ -1,5 +1,4 @@
import 'dart:convert';
import 'package:cwtch/config.dart';
import 'package:cwtch/notification_manager.dart';
import 'package:cwtch/views/messageview.dart';
import 'package:cwtch/widgets/rightshiftfixer.dart';
@ -29,7 +28,7 @@ var globalTorStatus = TorStatus();
var globalAppState = AppState();
void main() {
print("Cwtch version: ${EnvironmentConfig.BUILD_VER} built on: ${EnvironmentConfig.BUILD_DATE}");
print("main()");
LicenseRegistry.addLicense(() => licenses());
WidgetsFlutterBinding.ensureInitialized();
print("runApp()");
@ -169,7 +168,6 @@ class FlwtchState extends State<Flwtch> {
var args = jsonDecode(call.arguments);
var profile = profs.getProfile(args["ProfileOnion"])!;
var convo = profile.contactList.getContact(args["Handle"])!;
Provider.of<AppState>(navKey.currentContext!, listen: false).initialScrollIndex = convo.unreadMessages;
convo.unreadMessages = 0;
// single pane mode pushes; double pane mode reads AppState.selectedProfile/Conversation

View File

@ -3,6 +3,13 @@ import 'dart:convert';
import 'package:cwtch/widgets/messagerow.dart';
import 'package:flutter/cupertino.dart';
import 'package:cwtch/models/servers.dart';
import 'package:cwtch/widgets/messagebubble.dart';
import 'package:provider/provider.dart';
import 'dart:async';
import 'dart:collection';
import 'cwtch/cwtch.dart';
import 'main.dart';
////////////////////
/// UI State ///
@ -24,59 +31,6 @@ class ChatMessage {
};
}
class AppState extends ChangeNotifier {
bool cwtchInit = false;
bool cwtchIsClosing = false;
String appError = "";
String? _selectedProfile;
String? _selectedConversation;
int _initialScrollIndex = 0;
int? _selectedIndex;
bool _unreadMessagesBelow = false;
void SetCwtchInit() {
cwtchInit = true;
notifyListeners();
}
void SetAppError(String error) {
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();
}
int? get selectedIndex => _selectedIndex;
set selectedIndex(int? newVal) {
this._selectedIndex = newVal;
notifyListeners();
}
bool get unreadMessagesBelow => _unreadMessagesBelow;
set unreadMessagesBelow(bool newVal) {
this._unreadMessagesBelow = newVal;
notifyListeners();
}
int get initialScrollIndex => _initialScrollIndex;
set initialScrollIndex(int newVal) {
this._initialScrollIndex = newVal;
notifyListeners();
}
bool isLandscape(BuildContext c) => MediaQuery.of(c).size.width > MediaQuery.of(c).size.height;
}
///////////////////
/// Providers ///
///////////////////
@ -108,6 +62,45 @@ class ProfileListState extends ChangeNotifier {
}
}
class AppState extends ChangeNotifier {
bool cwtchInit = false;
bool cwtchIsClosing = false;
String appError = "";
String? _selectedProfile;
String? _selectedConversation;
int? _selectedIndex;
void SetCwtchInit() {
cwtchInit = true;
notifyListeners();
}
void SetAppError(String error) {
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();
}
int? get selectedIndex => _selectedIndex;
set selectedIndex(int? newVal) {
this._selectedIndex = newVal;
notifyListeners();
}
bool isLandscape(BuildContext c) => MediaQuery.of(c).size.width > MediaQuery.of(c).size.height;
}
class ContactListState extends ChangeNotifier {
List<ContactInfoState> _contacts = [];
String _filter = "";
@ -160,21 +153,12 @@ class ContactListState extends ChangeNotifier {
//} </todo>
}
void updateLastMessageTime(String forOnion, DateTime newMessageTime) {
void updateLastMessageTime(String forOnion, DateTime newVal) {
var contact = getContact(forOnion);
if (contact == null) return;
// Assert that the new time is after the current last message time AND that
// new message time is before the current time.
if (newMessageTime.isAfter(contact.lastMessageTime)) {
if (newMessageTime.isBefore(DateTime.now().toLocal())) {
contact.lastMessageTime = newMessageTime;
} else {
// Otherwise set the last message time to now...
contact.lastMessageTime = DateTime.now().toLocal();
}
resort();
}
contact.lastMessageTime = newVal;
resort();
}
List<ContactInfoState> get contacts => _contacts.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier
@ -229,7 +213,8 @@ class ProfileInfoState extends ChangeNotifier {
nickname: contact["name"],
status: contact["status"],
imagePath: contact["picture"],
authorization: stringToContactAuthorization(contact["authorization"]),
isBlocked: contact["authorization"] == "blocked",
isInvitation: contact["authorization"] == "unknown",
savePeerHistory: contact["saveConversationHistory"],
numMessages: contact["numMessages"],
numUnread: contact["numUnread"],
@ -331,7 +316,8 @@ class ProfileInfoState extends ChangeNotifier {
nickname: contact["name"],
status: contact["status"],
imagePath: contact["picture"],
authorization: stringToContactAuthorization(contact["authorization"]),
isBlocked: contact["authorization"] == "blocked",
isInvitation: contact["authorization"] == "unknown",
savePeerHistory: contact["saveConversationHistory"],
numMessages: contact["numMessages"],
numUnread: contact["numUnread"],
@ -345,25 +331,13 @@ class ProfileInfoState extends ChangeNotifier {
}
}
enum ContactAuthorization { unknown, approved, blocked }
ContactAuthorization stringToContactAuthorization(String authStr) {
switch (authStr) {
case "approved":
return ContactAuthorization.approved;
case "blocked":
return ContactAuthorization.blocked;
default:
return ContactAuthorization.unknown;
}
}
class ContactInfoState extends ChangeNotifier {
final String profileOnion;
final String onion;
late String _nickname;
late ContactAuthorization _authorization;
late bool _isInvitation;
late bool _isBlocked;
late String _status;
late String _imagePath;
late String _savePeerHistory;
@ -381,7 +355,8 @@ class ContactInfoState extends ChangeNotifier {
this.onion, {
nickname = "",
isGroup = false,
authorization = ContactAuthorization.unknown,
isInvitation = false,
isBlocked = false,
status = "",
imagePath = "",
savePeerHistory = "DeleteHistoryConfirmed",
@ -392,7 +367,8 @@ class ContactInfoState extends ChangeNotifier {
}) {
this._nickname = nickname;
this._isGroup = isGroup;
this._authorization = authorization;
this._isInvitation = isInvitation;
this._isBlocked = isBlocked;
this._status = status;
this._imagePath = imagePath;
this._totalMessages = numMessages;
@ -422,13 +398,15 @@ class ContactInfoState extends ChangeNotifier {
notifyListeners();
}
bool get isBlocked => this._authorization == ContactAuthorization.blocked;
bool get isBlocked => this._isBlocked;
set isBlocked(bool newVal) {
this._isBlocked = newVal;
notifyListeners();
}
bool get isInvitation => this._authorization == ContactAuthorization.unknown;
ContactAuthorization get authorization => this._authorization;
set authorization(ContactAuthorization newAuth) {
this._authorization = newAuth;
bool get isInvitation => this._isInvitation;
set isInvitation(bool newVal) {
this._isInvitation = newVal;
notifyListeners();
}

View File

@ -3,7 +3,6 @@ import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
import '../main.dart';
import '../model.dart';
import 'messages/invitemessage.dart';
import 'messages/malformedmessage.dart';
import 'messages/quotedmessage.dart';

View File

@ -25,7 +25,6 @@ class InviteMessage extends Message {
String inviteTarget;
String inviteNick;
String invite = this.content;
if (this.content.length == TorV3ContactHandleLength) {
inviteTarget = this.content;
@ -41,7 +40,7 @@ class InviteMessage extends Message {
return MessageRow(MalformedBubble());
}
}
return MessageRow(InvitationBubble(overlay, inviteTarget, inviteNick, invite), key: Provider.of<ContactInfoState>(bcontext).getMessageKey(idx));
return MessageRow(InvitationBubble(overlay, inviteTarget, inviteNick), key: Provider.of<ContactInfoState>(bcontext).getMessageKey(idx));
});
}
@ -52,7 +51,7 @@ class InviteMessage extends Message {
builder: (bcontext, child) {
String inviteTarget;
String inviteNick;
String invite = this.content;
if (this.content.length == TorV3ContactHandleLength) {
inviteTarget = this.content;
var targetContact = Provider.of<ProfileInfoState>(context).contactList.getContact(inviteTarget);
@ -67,7 +66,7 @@ class InviteMessage extends Message {
return MalformedBubble();
}
}
return InvitationBubble(overlay, inviteTarget, inviteNick, invite);
return InvitationBubble(overlay, inviteTarget, inviteNick);
});
}

View File

@ -10,17 +10,6 @@ import 'package:provider/provider.dart';
import '../../main.dart';
import '../../model.dart';
class QuotedMessageStructure {
final String quotedHash;
final String body;
QuotedMessageStructure(this.quotedHash, this.body);
Map<String, dynamic> toJson() => {
'quotedHash': quotedHash,
'body': body,
};
}
class LocallyIndexedMessage {
final dynamic message;
final int index;

View File

@ -1,7 +1,6 @@
import 'package:cwtch/models/message.dart';
import 'package:cwtch/widgets/messagebubble.dart';
import 'package:cwtch/widgets/messagerow.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';

View File

@ -354,7 +354,7 @@ class OpaqueDark extends OpaqueThemeType {
}
Color altTextColor() {
return mauvePurple;
return whitePurple;
}
Color hilightElementTextColor() {

View File

@ -136,12 +136,7 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
// We only allow setting password types on profile creation
Visibility(
visible: Provider.of<ProfileInfoState>(context).onion.isEmpty,
child: SizedBox(
height: 20,
)),
Visibility(
visible: Provider.of<ProfileInfoState>(context).onion.isEmpty,
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Checkbox(
value: usePassword,
fillColor: MaterialStateProperty.all(theme.current().defaultButtonColor()),
@ -152,15 +147,6 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
AppLocalizations.of(context)!.radioUsePassword,
style: TextStyle(color: theme.current().mainTextColor()),
),
SizedBox(
height: 20,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 24),
child: Text(
usePassword ? AppLocalizations.of(context)!.encryptedProfileDescription : AppLocalizations.of(context)!.plainProfileDescription,
textAlign: TextAlign.center,
))
])),
SizedBox(
height: 20,

View File

@ -12,8 +12,6 @@ import 'addcontactview.dart';
import '../model.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'messageview.dart';
class ContactsView extends StatefulWidget {
const ContactsView({Key? key}) : super(key: key);
@ -21,40 +19,6 @@ class ContactsView extends StatefulWidget {
_ContactsViewState createState() => _ContactsViewState();
}
// selectConversation can be called from anywhere to set the active conversation
void selectConversation(BuildContext context, String handle) {
// requery instead of using contactinfostate directly because sometimes listview gets confused about data that resorts
var initialIndex = Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.unreadMessages;
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.unreadMessages = 0;
// triggers update in Double/TripleColumnView
Provider.of<AppState>(context, listen: false).initialScrollIndex = initialIndex;
Provider.of<AppState>(context, listen: false).selectedConversation = handle;
Provider.of<AppState>(context, listen: false).selectedIndex = null;
// 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(context, handle);
}
void _pushMessageView(BuildContext context, String handle) {
var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext builderContext) {
// assert we have an actual profile...
// We need to listen for updates to the profile in order to update things like invitation message bubbles.
var profile = Provider.of<FlwtchState>(builderContext).profs.getProfile(profileOnion)!;
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: profile),
ChangeNotifierProvider.value(value: profile.contactList.getContact(handle)!),
],
builder: (context, child) => MessageView(),
);
},
),
);
}
class _ContactsViewState extends State<ContactsView> {
late TextEditingController ctrlrFilter;
bool showSearchBar = false;
@ -87,7 +51,18 @@ class _ContactsViewState extends State<ContactsView> {
child: Text("%1 » %2".replaceAll("%1", Provider.of<ProfileInfoState>(context).nickname).replaceAll("%2", AppLocalizations.of(context)!.titleManageContacts),
overflow: TextOverflow.ellipsis, style: TextStyle(color: Provider.of<Settings>(context).current().mainTextColor()))),
])),
actions: getActions(context),
actions: [
IconButton(icon: TorIcon(), onPressed: _pushTorStatus),
IconButton(
// need both conditions for displaying initial empty textfield and also allowing filters to be cleared if this widget gets lost/reset
icon: Icon(showSearchBar || Provider.of<ContactListState>(context).isFiltered ? Icons.search_off : Icons.search),
onPressed: () {
Provider.of<ContactListState>(context, listen: false).filter = "";
setState(() {
showSearchBar = !showSearchBar;
});
})
],
),
floatingActionButton: FloatingActionButton(
onPressed: _pushAddContact,
@ -97,26 +72,6 @@ class _ContactsViewState extends State<ContactsView> {
body: showSearchBar || Provider.of<ContactListState>(context).isFiltered ? _buildFilterable() : _buildContactList());
}
List<Widget> getActions(context) {
var actions = List<Widget>.empty(growable: true);
if (Provider.of<Settings>(context).blockUnknownConnections) {
actions.add(Tooltip(message: AppLocalizations.of(context)!.blockUnknownConnectionsEnabledDescription, child: Icon(CwtchIcons.block_unknown)));
}
actions.add(
IconButton(icon: TorIcon(), onPressed: _pushTorStatus),
);
actions.add(IconButton(
// need both conditions for displaying initial empty textfield and also allowing filters to be cleared if this widget gets lost/reset
icon: Icon(showSearchBar || Provider.of<ContactListState>(context).isFiltered ? Icons.search_off : Icons.search),
onPressed: () {
Provider.of<ContactListState>(context, listen: false).filter = "";
setState(() {
showSearchBar = !showSearchBar;
});
}));
return actions;
}
Widget _buildFilterable() {
Widget txtfield = CwtchTextField(
controller: ctrlrFilter,

View File

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

View File

@ -34,7 +34,6 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
Widget _buildSettingsList() {
return Consumer<Settings>(builder: (context, settings, child) {
return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) {
var appIcon = Icon(Icons.info, color: settings.current().mainTextColor());
return Scrollbar(
isAlwaysShown: true,
child: SingleChildScrollView(
@ -94,31 +93,20 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
);
}).toList())),
ListTile(
title: Text(
AppLocalizations.of(context)!.settingUIColumnLandscape,
textWidthBasis: TextWidthBasis.longestLine,
softWrap: true,
style: TextStyle(color: settings.current().mainTextColor()),
),
title: Text(AppLocalizations.of(context)!.settingUIColumnLandscape, style: TextStyle(color: settings.current().mainTextColor())),
leading: Icon(Icons.table_chart, color: settings.current().mainTextColor()),
trailing: Container(
width: 200.0,
child: DropdownButton(
isExpanded: true,
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, context),
overflow: TextOverflow.ellipsis,
),
);
}).toList()))),
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, context)),
);
}).toList())),
SwitchListTile(
title: Text(AppLocalizations.of(context)!.blockUnknownLabel, style: TextStyle(color: settings.current().mainTextColor())),
subtitle: Text(AppLocalizations.of(context)!.descriptionBlockUnknownConnections),
@ -178,17 +166,12 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
],
)),
AboutListTile(
icon: appIcon,
applicationIcon: Padding(padding: EdgeInsets.all(5), child: Icon(CwtchIcons.cwtch_knott)),
applicationName: "Cwtch (Flutter UI)",
applicationLegalese: '\u{a9} 2021 Open Privacy Research Society',
aboutBoxChildren: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(
24.0 + 10.0 + (appIcon.size ?? 24.0), 16.0, 0.0, 0.0), // About has 24 padding (ln 389) and there appears to be another 10 of padding in the widget
child: SelectableText(AppLocalizations.of(context)!.versionBuilddate.replaceAll("%1", EnvironmentConfig.BUILD_VER).replaceAll("%2", EnvironmentConfig.BUILD_DATE)),
)
]),
icon: Icon(Icons.info, color: settings.current().mainTextColor()),
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

@ -173,7 +173,6 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
var handle = Provider.of<ContactInfoState>(context, listen: false).onion;
Provider.of<FlwtchState>(context, listen: false).cwtch.LeaveGroup(profileOnion, handle);
Future.delayed(Duration(milliseconds: 500), () {
Provider.of<AppState>(context, listen: false).selectedConversation = null;
Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog
});
},

View File

@ -3,7 +3,7 @@ import 'dart:io';
import 'package:crypto/crypto.dart';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:cwtch/models/message.dart';
import 'package:cwtch/models/messages/quotedmessage.dart';
import 'package:cwtch/widgets/malformedbubble.dart';
import 'package:cwtch/widgets/messageloadingbubble.dart';
import 'package:cwtch/widgets/profileimage.dart';
import 'package:flutter/cupertino.dart';
@ -13,7 +13,6 @@ import 'package:cwtch/widgets/DropdownContacts.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import '../main.dart';
import '../model.dart';
@ -30,35 +29,14 @@ class _MessageViewState extends State<MessageView> {
final ctrlrCompose = TextEditingController();
final focusNode = FocusNode();
String selectedContact = "";
ItemPositionsListener scrollListener = ItemPositionsListener.create();
ItemScrollController scrollController = ItemScrollController();
@override
void initState() {
scrollListener.itemPositions.addListener(() {
var first = scrollListener.itemPositions.value.first.index;
var last = scrollListener.itemPositions.value.last.index;
// sometimes these go hi->lo and sometimes they go lo->hi because [who tf knows]
if ((first == 0 || last == 0) && Provider.of<AppState>(context, listen: false).unreadMessagesBelow == true) {
Provider.of<AppState>(context, listen: false).initialScrollIndex = 0;
Provider.of<AppState>(context, listen: false).unreadMessagesBelow = false;
}
});
super.initState();
}
@override
void didChangeDependencies() {
var appState = Provider.of<AppState>(context, listen: false);
// using "8" because "# of messages that fit on one screen" isnt trivial to calculate at this point
if (appState.initialScrollIndex > 8 && appState.unreadMessagesBelow == false) {
WidgetsFlutterBinding.ensureInitialized().addPostFrameCallback((timeStamp) {
appState.unreadMessagesBelow = true;
});
}
super.didChangeDependencies();
}
// @override
// void didChangeDependencies() {
// super.didChangeDependencies();
// if (Provider.of<ContactInfoState>(context, listen: false).unreadMessages > 0) {
// Provider.of<ContactInfoState>(context, listen: false).unreadMessages = 0;
// }
// }
@override
void dispose() {
@ -69,6 +47,7 @@ class _MessageViewState extends State<MessageView> {
@override
Widget build(BuildContext context) {
// After leaving a conversation the selected conversation is set to null...
if (Provider.of<ContactInfoState>(context).profileOnion == "") {
return Card(child: Center(child: Text(AppLocalizations.of(context)!.addContactFirst)));
@ -78,13 +57,6 @@ class _MessageViewState extends State<MessageView> {
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
floatingActionButton: appState.unreadMessagesBelow
? FloatingActionButton(
child: Icon(Icons.arrow_downward),
onPressed: () {
scrollController.scrollTo(index: 0, duration: Duration(milliseconds: 600));
})
: null,
appBar: AppBar(
// 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,
@ -99,36 +71,26 @@ class _MessageViewState extends State<MessageView> {
SizedBox(
width: 10,
),
Expanded(
child: Text(
Provider.of<ContactInfoState>(context).nickname,
overflow: TextOverflow.ellipsis,
))
Expanded(
child:Text(Provider.of<ContactInfoState>(context).nickname, overflow: TextOverflow.ellipsis,))
]),
actions: [
//IconButton(icon: Icon(Icons.chat), onPressed: _pushContactSettings),
//IconButton(icon: Icon(Icons.list), onPressed: _pushContactSettings),
//IconButton(icon: Icon(Icons.push_pin), onPressed: _pushContactSettings),
IconButton(
icon: Icon(CwtchIcons.send_invite, size: 24),
tooltip: AppLocalizations.of(context)!.sendInvite,
onPressed: () {
_modalSendInvitation(context);
}),
IconButton(
icon: Provider.of<ContactInfoState>(context, listen: false).isGroup == true ? Icon(CwtchIcons.group_settings_24px) : Icon(CwtchIcons.peer_settings_24px),
tooltip: AppLocalizations.of(context)!.conversationSettings,
onPressed: _pushContactSettings),
],
),
body: Padding(padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 108.0), child: MessageList(scrollController, scrollListener)),
body: Padding(padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 108.0), child: MessageList()),
bottomSheet: _buildComposeBox(),
));
}
Future<bool> _onWillPop() async {
Provider.of<ContactInfoState>(context, listen: false).unreadMessages = 0;
Provider.of<AppState>(context, listen: false).selectedConversation = null;
return true;
}
@ -163,7 +125,7 @@ class _MessageViewState extends State<MessageView> {
var bytes1 = utf8.encode(messageWrapper["PeerID"] + messageWrapper['Message']);
var digest1 = sha256.convert(bytes1);
var contentHash = base64Encode(digest1.bytes);
var quotedMessage = jsonEncode(QuotedMessageStructure(contentHash, ctrlrCompose.value.text));
var quotedMessage = "{\"quotedHash\":\"" + contentHash + "\",\"body\":\"" + ctrlrCompose.value.text + "\"}";
ChatMessage cm = new ChatMessage(o: QuotedMessageOverlay, d: quotedMessage);
Provider.of<FlwtchState>(context, listen: false)
.cwtch
@ -200,8 +162,6 @@ class _MessageViewState extends State<MessageView> {
}
Widget _buildComposeBox() {
bool isOffline = Provider.of<ContactInfoState>(context).isOnline() == false;
var composeBox = Container(
color: Provider.of<Settings>(context).theme.backgroundMainColor(),
padding: EdgeInsets.all(2),
@ -210,37 +170,39 @@ class _MessageViewState extends State<MessageView> {
child: Row(
children: <Widget>[
Expanded(
child: Container(
decoration: BoxDecoration(border: Border(top: BorderSide(color: Provider.of<Settings>(context).theme.defaultButtonActiveColor()))),
child: RawKeyboardListener(
focusNode: FocusNode(),
onKey: handleKeyPress,
child: Padding(
padding: EdgeInsets.all(8),
child: TextFormField(
key: Key('txtCompose'),
controller: ctrlrCompose,
focusNode: focusNode,
autofocus: !Platform.isAndroid,
textInputAction: TextInputAction.newline,
keyboardType: TextInputType.multiline,
enableIMEPersonalizedLearning: false,
minLines: 1,
maxLines: null,
onFieldSubmitted: _sendMessage,
enabled: !isOffline,
decoration: InputDecoration(
hintText: isOffline ? "" : AppLocalizations.of(context)!.placeholderEnterMessage,
hintStyle: TextStyle(color: Provider.of<Settings>(context).theme.altTextColor()),
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
enabled: true,
suffixIcon: ElevatedButton(
style: ElevatedButton.styleFrom(primary: Provider.of<Settings>(context).theme.defaultButtonColor()),
child: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of<Settings>(context).theme.mainTextColor()),
onPressed: isOffline ? null : _sendMessage,
))),
)))),
child: Container(
decoration: BoxDecoration(border: Border(top: BorderSide(color: Provider.of<Settings>(context).theme.defaultButtonActiveColor()))),
child: RawKeyboardListener(
focusNode: FocusNode(),
onKey: handleKeyPress,
child: TextFormField(
key: Key('txtCompose'),
controller: ctrlrCompose,
focusNode: focusNode,
autofocus: !Platform.isAndroid,
textInputAction: TextInputAction.newline,
keyboardType: TextInputType.multiline,
minLines: 1,
maxLines: null,
onFieldSubmitted: _sendMessage,
decoration: InputDecoration(
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
enabled: true,
prefixIcon: IconButton(
icon: Icon(CwtchIcons.send_invite, size: 24, color: Provider.of<Settings>(context).theme.mainTextColor()),
tooltip: AppLocalizations.of(context)!.sendInvite,
enableFeedback: true,
splashColor: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
hoverColor: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
onPressed: () => _modalSendInvitation(context)),
suffixIcon: IconButton(
icon: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of<Settings>(context).theme.mainTextColor()),
tooltip: AppLocalizations.of(context)!.sendMessage,
onPressed: _sendMessage,
),
)))),
),
],
),
);

View File

@ -114,11 +114,7 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
value: Provider.of<ContactInfoState>(context).isBlocked,
onChanged: (bool blocked) {
// Save local blocked status
if (blocked) {
Provider.of<ContactInfoState>(context, listen: false).authorization = ContactAuthorization.blocked;
} else {
Provider.of<ContactInfoState>(context, listen: false).authorization = ContactAuthorization.unknown;
}
Provider.of<ContactInfoState>(context, listen: false).isBlocked = blocked;
// Save New peer authorization
var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
@ -220,7 +216,7 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
showAlertDialog(BuildContext context) {
// set up the buttons
Widget cancelButton = TextButton(
child: Text(AppLocalizations.of(context)!.cancel),
child: Text("Cancel"),
style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))),
onPressed: () {
Navigator.of(context).pop(); // dismiss dialog
@ -234,7 +230,6 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
var handle = Provider.of<ContactInfoState>(context, listen: false).onion;
Provider.of<FlwtchState>(context, listen: false).cwtch.LeaveConversation(profileOnion, handle);
Future.delayed(Duration(milliseconds: 500), () {
Provider.of<AppState>(context, listen: false).selectedConversation = null;
Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog
});
},

View File

@ -39,6 +39,7 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
return Consumer<Settings>(
// 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 {
_modalShutdown();
@ -48,9 +49,14 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
backgroundColor: settings.theme.backgroundMainColor(),
appBar: AppBar(
title: Row(children: [
Icon(
CwtchIcons.cwtch_knott,
size: 36,
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,
@ -89,7 +95,6 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
// Unlock Profiles
actions.add(IconButton(
icon: Icon(CwtchIcons.lock_open_24px),
color: Provider.of<ProfileListState>(context).profiles.isEmpty ? Provider.of<Settings>(context).theme.defaultButtonColor() : Provider.of<Settings>(context).theme.mainTextColor(),
tooltip: AppLocalizations.of(context)!.tooltipUnlockProfiles,
onPressed: _modalUnlockProfiles,
));

View File

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

View File

@ -37,7 +37,6 @@ class _CwtchButtonTextFieldState extends State<CwtchButtonTextField> {
readOnly: widget.readonly,
showCursor: !widget.readonly,
focusNode: _focusNode,
enableIMEPersonalizedLearning: false,
decoration: InputDecoration(
suffixIcon: IconButton(
onPressed: widget.onPressed,

View File

@ -1,6 +1,3 @@
import 'dart:io';
import 'package:cwtch/views/contactsview.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:cwtch/views/messageview.dart';
@ -25,11 +22,10 @@ class _ContactRowState extends State<ContactRow> {
clipBehavior: Clip.antiAlias,
color: Provider.of<AppState>(context).selectedConversation == contact.onion ? Provider.of<Settings>(context).theme.backgroundHilightElementColor() : null,
borderOnForeground: false,
margin: EdgeInsets.all(0.0),
child: InkWell(
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Padding(
padding: const EdgeInsets.all(6.0), //border size
padding: const EdgeInsets.all(2.0), //border size
child: ProfileImage(
badgeCount: contact.unreadMessages,
badgeColor: Provider.of<Settings>(context).theme.portraitContactBadgeColor(),
@ -60,11 +56,6 @@ class _ContactRowState extends State<ContactRow> {
softWrap: true,
overflow: TextOverflow.visible,
),
Visibility(
visible: contact.isGroup && contact.status == "Authenticated",
child: LinearProgressIndicator(
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
)),
Text(contact.onion,
style: TextStyle(color: contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedTextColor() : Provider.of<Settings>(context).theme.mainTextColor())),
],
@ -102,11 +93,40 @@ class _ContactRowState extends State<ContactRow> {
),
]),
onTap: () {
selectConversation(context, contact.onion);
setState(() {
// 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;
Provider.of<AppState>(context, listen: false).selectedIndex = null;
// 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) {
var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext builderContext) {
// assert we have an actual profile...
// We need to listen for updates to the profile in order to update things like invitation message bubbles.
var profile = Provider.of<FlwtchState>(builderContext).profs.getProfile(profileOnion)!;
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: profile),
ChangeNotifierProvider.value(value: profile.contactList.getContact(handle)!),
],
builder: (context, child) => MessageView(),
);
},
),
);
}
void _btnApprove() {
Provider.of<FlwtchState>(context, listen: false)
.cwtch
@ -129,9 +149,9 @@ class _ContactRowState extends State<ContactRow> {
}
// If the last message was over a day ago, just state the date
if (DateTime.now().difference(date).inDays > 1) {
return DateFormat.yMd(Platform.localeName).format(date.toLocal());
return DateFormat.yMd().format(date.toLocal());
}
// Otherwise just state the time.
return DateFormat.Hm(Platform.localeName).format(date.toLocal());
return DateFormat.Hm().format(date.toLocal());
}
}

View File

@ -1,5 +1,4 @@
import 'dart:convert';
import 'dart:io';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:cwtch/models/message.dart';
@ -20,9 +19,8 @@ class InvitationBubble extends StatefulWidget {
final int overlay;
final String inviteTarget;
final String inviteNick;
final String invite;
InvitationBubble(this.overlay, this.inviteTarget, this.inviteNick, this.invite);
InvitationBubble(this.overlay, this.inviteTarget, this.inviteNick);
@override
InvitationBubbleState createState() => InvitationBubbleState();
@ -40,7 +38,7 @@ class InvitationBubbleState extends State<InvitationBubble> {
var borderRadiousEh = 15.0;
var showGroupInvite = Provider.of<Settings>(context).isExperimentEnabled(TapirGroupsExperiment);
rejected = Provider.of<MessageMetadata>(context).flags & 0x01 == 0x01;
var prettyDate = DateFormat.yMd(Platform.localeName).add_jm().format(Provider.of<MessageMetadata>(context).timestamp);
var prettyDate = DateFormat.yMd().add_jm().format(Provider.of<MessageMetadata>(context).timestamp);
// If the sender is not us, then we want to give them a nickname...
var senderDisplayStr = "";
@ -138,7 +136,7 @@ class InvitationBubbleState extends State<InvitationBubble> {
void _btnAccept() {
setState(() {
var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
Provider.of<FlwtchState>(context, listen: false).cwtch.ImportBundle(profileOnion, widget.invite);
Provider.of<FlwtchState>(context, listen: false).cwtch.ImportBundle(profileOnion, widget.inviteTarget);
isAccepted = true;
});
}

View File

@ -1,5 +1,3 @@
import 'dart:io';
import 'package:cwtch/models/message.dart';
import 'package:cwtch/widgets/malformedbubble.dart';
import 'package:flutter/material.dart';
@ -30,7 +28,7 @@ class MessageBubbleState extends State<MessageBubble> {
// var myKey = Provider.of<MessageState>(context).profileOnion + "::" + Provider.of<MessageState>(context).contactHandle + "::" + Provider.of<MessageState>(context).messageIndex.toString();
DateTime messageDate = Provider.of<MessageMetadata>(context).timestamp;
prettyDate = DateFormat.yMd(Platform.localeName).add_jm().format(messageDate.toLocal());
prettyDate = DateFormat.yMd().add_jm().format(messageDate.toLocal());
// If the sender is not us, then we want to give them a nickname...
var senderDisplayStr = "";

View File

@ -45,5 +45,6 @@ class _MessageBubbleDecoration extends State<MessageBubbleDecoration> {
child: Icon(Icons.hourglass_bottom_outlined, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16))))
],
));
;
}
}

View File

@ -1,26 +1,26 @@
import 'package:cwtch/models/message.dart';
import 'package:cwtch/models/messages/malformedmessage.dart';
import 'package:cwtch/widgets/malformedbubble.dart';
import 'package:cwtch/widgets/messageloadingbubble.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:provider/provider.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../main.dart';
import '../model.dart';
import '../settings.dart';
import 'messagerow.dart';
class MessageList extends StatefulWidget {
ItemScrollController scrollController;
ItemPositionsListener scrollListener;
MessageList(this.scrollController, this.scrollListener);
@override
_MessageListState createState() => _MessageListState();
}
class _MessageListState extends State<MessageList> {
ScrollController ctrlr1 = ScrollController();
@override
Widget build(BuildContext outerContext) {
var initi = Provider.of<AppState>(outerContext, listen: false).initialScrollIndex;
bool isP2P = !Provider.of<ContactInfoState>(context).isGroup;
bool isGroupAndSyncing = Provider.of<ContactInfoState>(context).isGroup == true && Provider.of<ContactInfoState>(context).status == "Authenticated";
bool isGroupAndSynced = Provider.of<ContactInfoState>(context).isGroup && Provider.of<ContactInfoState>(context).status == "Synced";
@ -39,24 +39,23 @@ class _MessageListState extends State<MessageList> {
Visibility(
visible: showMessageWarning,
child: Container(
padding: EdgeInsets.all(5.0),
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
child: DefaultTextStyle(
style: TextStyle(color: Provider.of<Settings>(context).theme.defaultButtonTextColor()),
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...
: (showEphemeralWarning
? Text(AppLocalizations.of(context)!.chatHistoryDefault, textAlign: TextAlign.center)
:
// We are not allowed to put null here, so put an empty text widget
Text("")),
))),
padding: EdgeInsets.all(5.0),
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
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...
: (showEphemeralWarning
? Text(AppLocalizations.of(context)!.chatHistoryDefault, textAlign: TextAlign.center)
:
// We are not allowed to put null here, so put an empty text widge
Text("")),
)),
Expanded(
child: Scrollbar(
controller: ctrlr1,
child: Container(
// Only show broken heart is the contact is offline...
decoration: BoxDecoration(
@ -69,10 +68,8 @@ class _MessageListState extends State<MessageList> {
colorFilter: ColorFilter.mode(Provider.of<Settings>(context).theme.hilightElementTextColor(), BlendMode.srcIn))),
// Don't load messages for syncing server...
child: loadMessages
? ScrollablePositionedList.builder(
itemPositionsListener: widget.scrollListener,
itemScrollController: widget.scrollController,
initialScrollIndex: Provider.of<AppState>(outerContext, listen: false).initialScrollIndex,
? ListView.builder(
controller: ctrlr1,
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...
itemBuilder: (itemBuilderContext, index) {

View File

@ -1,11 +1,8 @@
import 'dart:io';
import 'dart:convert';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:cwtch/models/message.dart';
import 'package:cwtch/views/contactsview.dart';
import 'package:flutter/material.dart';
import 'package:cwtch/widgets/profileimage.dart';
import 'package:flutter/physics.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@ -21,127 +18,34 @@ class MessageRow extends StatefulWidget {
MessageRowState createState() => MessageRowState();
}
class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMixin {
class MessageRowState extends State<MessageRow> {
bool showMenu = false;
bool showBlockedMessage = false;
late AnimationController _controller;
late Animation<Alignment> _animation;
late Alignment _dragAlignment = Alignment.center;
Alignment _dragAffinity = Alignment.center;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
_controller.addListener(() {
setState(() {
_dragAlignment = _animation.value;
});
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
var fromMe = Provider.of<MessageMetadata>(context).senderHandle == Provider.of<ProfileInfoState>(context).onion;
var isContact = Provider.of<ProfileInfoState>(context).contactList.getContact(Provider.of<MessageMetadata>(context).senderHandle) != null;
var isBlocked = isContact ? Provider.of<ProfileInfoState>(context).contactList.getContact(Provider.of<MessageMetadata>(context).senderHandle)!.isBlocked : false;
var actualMessage = Flexible(flex: 3, fit: FlexFit.loose, child: widget.child);
_dragAffinity = fromMe ? Alignment.centerRight : Alignment.centerLeft;
if (_dragAlignment == Alignment.center) {
_dragAlignment = fromMe ? Alignment.centerRight : Alignment.centerLeft;
}
var senderDisplayStr = "";
if (!fromMe) {
ContactInfoState? contact = Provider.of<ProfileInfoState>(context).contactList.getContact(Provider.of<MessageMetadata>(context).senderHandle);
if (contact != null) {
senderDisplayStr = contact.nickname;
} else {
senderDisplayStr = Provider.of<MessageMetadata>(context).senderHandle;
}
}
Widget wdgIcons = Visibility(
visible: this.showMenu,
maintainSize: true,
maintainAnimation: true,
maintainState: true,
maintainInteractivity: false,
child: IconButton(
tooltip: AppLocalizations.of(context)!.tooltipReplyToThisMessage,
onPressed: () {
Provider.of<AppState>(context, listen: false).selectedIndex = Provider.of<MessageMetadata>(context, listen: false).messageIndex;
Provider.of<AppState>(context, listen: false).selectedIndex = Provider.of<MessageMetadata>(context).messageIndex;
},
icon: Icon(Icons.reply, color: Provider.of<Settings>(context).theme.dropShadowColor())));
Widget wdgSpacer = Flexible(child: SizedBox(width: 60, height: 10));
Widget wdgSpacer = Expanded(child: SizedBox(width: 60, height: 10));
var widgetRow = <Widget>[];
if (fromMe) {
widgetRow = <Widget>[
wdgSpacer,
wdgIcons,
actualMessage,
];
} else if (isBlocked && !showBlockedMessage) {
Color blockedMessageBackground = Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor();
Widget wdgPortrait = Padding(padding: EdgeInsets.all(4.0), child: Icon(CwtchIcons.account_blocked));
widgetRow = <Widget>[
wdgPortrait,
Container(
padding: EdgeInsets.all(2.0),
decoration: BoxDecoration(
color: blockedMessageBackground,
border: Border.all(color: blockedMessageBackground, width: 2),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15.0),
topRight: Radius.circular(15.0),
bottomLeft: Radius.circular(15.0),
bottomRight: Radius.circular(15.0),
)),
child: Padding(
padding: EdgeInsets.all(9.0),
child: Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
SelectableText(
AppLocalizations.of(context)!.blockedMessageMessage,
//key: Key(myKey),
style: TextStyle(
color: Provider.of<Settings>(context).theme.messageFromOtherTextColor(),
),
textAlign: TextAlign.center,
textWidthBasis: TextWidthBasis.longestLine,
),
Padding(
padding: EdgeInsets.all(1.0),
child: TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(blockedMessageBackground),
overlayColor: MaterialStateProperty.all(blockedMessageBackground),
),
child: Text(
AppLocalizations.of(context)!.showMessageButton + '\u202F',
style: TextStyle(decoration: TextDecoration.underline),
),
onPressed: () {
setState(() {
this.showBlockedMessage = true;
});
})),
]))),
wdgIcons,
wdgSpacer,
Flexible(flex: 3, fit: FlexFit.loose, child: widget.child),
];
} else {
var contact = Provider.of<ContactInfoState>(context);
Widget wdgPortrait = GestureDetector(
onTap: isContact ? _btnGoto : _btnAdd,
onTap: _btnAdd,
child: Padding(
padding: EdgeInsets.all(4.0),
child: ProfileImage(
@ -150,19 +54,19 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
//maskOut: contact.status != "Authenticated",
border: contact.status == "Authenticated" ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor() : Provider.of<Settings>(context).theme.portraitOfflineBorderColor(),
badgeTextColor: Colors.red, badgeColor: Colors.red,
tooltip: isContact ? AppLocalizations.of(context)!.contactGoto.replaceFirst("%1", senderDisplayStr) : AppLocalizations.of(context)!.addContact,
)));
widgetRow = <Widget>[
wdgPortrait,
actualMessage,
Flexible(flex: 3, fit: FlexFit.loose, child: widget.child),
wdgIcons,
wdgSpacer,
];
}
var size = MediaQuery.of(context).size;
return MouseRegion(
// For desktop...
onHover: (event) {
setState(() {
this.showMenu = true;
@ -174,59 +78,12 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
});
},
child: GestureDetector(
onPanUpdate: (details) {
setState(() {
_dragAlignment += Alignment(
details.delta.dx / (size.width * 0.5),
0,
);
});
},
onPanDown: (details) {
_controller.stop();
},
onPanEnd: (details) {
_runAnimation(details.velocity.pixelsPerSecond, size);
// Swipe to quote
onHorizontalDragEnd: (details) {
Provider.of<AppState>(context, listen: false).selectedIndex = Provider.of<MessageMetadata>(context, listen: false).messageIndex;
},
child: Padding(
padding: EdgeInsets.all(2),
child: Align(
widthFactor: 1,
alignment: _dragAlignment,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: widgetRow,
)))));
}
void _runAnimation(Offset pixelsPerSecond, Size size) {
_animation = _controller.drive(
AlignmentTween(
begin: _dragAlignment,
end: _dragAffinity,
),
);
// Calculate the velocity relative to the unit interval, [0,1],
// used by the animation controller.
final unitsPerSecondX = pixelsPerSecond.dx / size.width;
final unitsPerSecondY = pixelsPerSecond.dy / size.height;
final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY);
final unitVelocity = unitsPerSecond.distance;
const spring = SpringDescription(
mass: 30,
stiffness: 1,
damping: 1,
);
final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);
_controller.animateWith(simulation);
}
void _btnGoto() {
selectConversation(context, Provider.of<MessageMetadata>(context, listen: false).senderHandle);
child: Padding(padding: EdgeInsets.all(2), child: Row(mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, children: widgetRow))));
}
void _btnAdd() {
@ -235,49 +92,19 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
print("sender not yet loaded");
return;
}
var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
final setPeerAttribute = {
"EventType": "AddContact",
"Data": {"ImportString": sender},
};
final setPeerAttributeJson = jsonEncode(setPeerAttribute);
Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(profileOnion, setPeerAttributeJson);
showAddContactConfirmAlertDialog(context, profileOnion, sender);
}
showAddContactConfirmAlertDialog(BuildContext context, String profileOnion, String senderOnion) {
// set up the buttons
Widget cancelButton = TextButton(
child: Text(AppLocalizations.of(context)!.cancel),
style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))),
onPressed: () {
Navigator.of(context).pop(); // dismiss dialog
},
);
Widget continueButton = TextButton(
style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))),
child: Text(AppLocalizations.of(context)!.addContact),
onPressed: () {
Provider.of<FlwtchState>(context, listen: false).cwtch.ImportBundle(profileOnion, senderOnion);
final snackBar = SnackBar(
content: Text(AppLocalizations.of(context)!.successfullAddedContact),
duration: Duration(seconds: 2),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
Navigator.of(context).pop(); // dismiss dialog
},
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text(AppLocalizations.of(context)!.addContactConfirm.replaceFirst("%1", senderOnion)),
actions: [
cancelButton,
continueButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
final snackBar = SnackBar(
content: Text(AppLocalizations.of(context)!.successfullAddedContact),
duration: Duration(seconds: 2),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}

View File

@ -37,7 +37,6 @@ class _CwtchTextFieldState extends State<CwtchPasswordField> {
controller: widget.controller,
validator: widget.validator,
obscureText: obscureText,
enableIMEPersonalizedLearning: false,
autofillHints: widget.autoFillHints,
autovalidateMode: AutovalidateMode.always,
onFieldSubmitted: widget.action,

View File

@ -5,8 +5,7 @@ import 'package:provider/provider.dart';
import '../settings.dart';
class ProfileImage extends StatefulWidget {
ProfileImage(
{required this.imagePath, required this.diameter, required this.border, this.badgeCount = 0, required this.badgeColor, required this.badgeTextColor, this.maskOut = false, this.tooltip = ""});
ProfileImage({required this.imagePath, required this.diameter, required this.border, this.badgeCount = 0, required this.badgeColor, required this.badgeTextColor, this.maskOut = false});
final double diameter;
final String imagePath;
final Color border;
@ -14,7 +13,6 @@ class ProfileImage extends StatefulWidget {
final Color badgeColor;
final Color badgeTextColor;
final bool maskOut;
final String tooltip;
@override
_ProfileImageState createState() => _ProfileImageState();
@ -23,21 +21,6 @@ class ProfileImage extends StatefulWidget {
class _ProfileImageState extends State<ProfileImage> {
@override
Widget build(BuildContext context) {
var image = Image(
image: AssetImage("assets/" + widget.imagePath),
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.identifier() == "dark"
? BlendMode.softLight
: BlendMode.darken
: BlendMode.srcOut,
color: Provider.of<Settings>(context).theme.backgroundHilightElementColor(),
isAntiAlias: true,
width: widget.diameter,
height: widget.diameter,
);
return RepaintBoundary(
child: Stack(children: [
ClipOval(
@ -48,7 +31,22 @@ class _ProfileImageState extends State<ProfileImage> {
color: widget.border,
child: Padding(
padding: const EdgeInsets.all(2.0), //border size
child: ClipOval(clipBehavior: Clip.antiAlias, child: widget.tooltip == "" ? image : Tooltip(message: widget.tooltip, child: image))))),
child: ClipOval(
clipBehavior: Clip.antiAlias,
child: Image(
image: AssetImage("assets/" + widget.imagePath),
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.identifier() == "dark"
? BlendMode.softLight
: BlendMode.darken
: BlendMode.srcOut,
color: Provider.of<Settings>(context).theme.backgroundHilightElementColor(),
isAntiAlias: true,
width: widget.diameter,
height: widget.diameter,
))))),
Visibility(
visible: widget.badgeCount > 0,
child: Positioned(

View File

@ -22,13 +22,12 @@ class _ProfileRowState extends State<ProfileRow> {
var profile = Provider.of<ProfileInfoState>(context);
return Card(
clipBehavior: Clip.antiAlias,
margin: EdgeInsets.all(0.0),
child: InkWell(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.all(6.0), //border size
padding: const EdgeInsets.all(2.0), //border size
child: ProfileImage(
badgeCount: 0,
badgeColor: Provider.of<Settings>(context).theme.portraitProfileBadgeColor(),

View File

@ -61,14 +61,13 @@ class QuotedMessageBubbleState extends State<QuotedMessageBubble> {
try {
var qMessage = (snapshot.data! as Message);
// Swap the background color for quoted tweets..
var qTextColor = fromMe ? Provider.of<Settings>(context).theme.messageFromOtherTextColor() : Provider.of<Settings>(context).theme.messageFromMeTextColor();
return Container(
margin: EdgeInsets.all(5),
padding: EdgeInsets.all(5),
color: fromMe ? Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor() : Provider.of<Settings>(context).theme.messageFromMeBackgroundColor(),
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(Icons.reply, size: 32, color: qTextColor))),
Center(widthFactor: 1.0, child: DefaultTextStyle(child: qMessage.getPreviewWidget(context), style: TextStyle(color: qTextColor)))
Center(widthFactor: 1, child: Padding(padding: EdgeInsets.all(10.0), child: Icon(Icons.reply, size: 32))),
Center(widthFactor: 1.0, child: qMessage.getPreviewWidget(context))
]));
} catch (e) {
print(e);

View File

@ -39,7 +39,6 @@ class _CwtchTextFieldState extends State<CwtchTextField> {
validator: widget.validator,
onChanged: widget.onChanged,
autofocus: widget.autofocus,
enableIMEPersonalizedLearning: false,
focusNode: _focusNode,
decoration: InputDecoration(
labelText: widget.labelText,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -21,7 +21,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.1"
version: "2.7.0"
boolean_selector:
dependency: transitive
description:
@ -181,7 +181,7 @@ packages:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
version: "1.4.0"
msix:
dependency: "direct dev"
description:
@ -329,13 +329,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "5.0.0"
scrollable_positioned_list:
dependency: "direct main"
description:
name: scrollable_positioned_list
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0-nullsafety.0"
sky_engine:
dependency: transitive
description: flutter
@ -382,7 +375,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.2"
version: "0.4.1"
typed_data:
dependency: transitive
description:

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.1.1+18
version: 1.0.0+16
environment:
sdk: ">=2.12.0 <3.0.0"
@ -40,7 +40,6 @@ dependencies:
glob: any
flutter_test:
sdk: flutter
scrollable_positioned_list: ^0.2.0-nullsafety.0
dev_dependencies:
msix: ^2.1.3

View File

@ -1,3 +0,0 @@
#!/bin/sh
flutter format -l 200 lib

View File

@ -1,38 +0,0 @@
#!/bin/sh
orig=assets/core/knott-white.svg
transparent=assets/core/knott-transparent.svg
# app icon used in profile manager bar
inkscape -w 512 -h 512 -o assets/core/knott-white.png $orig
inkscape -w 512 -h 512 -o assets/core/knott-transparent.png transparent
# linux deploy icon
inkscape -w 512 -h 512 -o linux/cwtch.png $orig
inkscape -w 512 -h 512 -o cwtch.png $orig
# windows icons
inkscape -w 256 -h 256 -o windows/runner/resources/knot_256.png $orig
convert windows/runner/resources/knot_256.png windows/runner/resources/knot_256.ico
inkscape -w 128 -h 128 -o windows/runner/resources/knot_128.png $orig
convert windows/runner/resources/knot_128.png windows/runner/resources/knot_128.ico
inkscape -w 64 -h 64 -o windows/runner/resources/knot_64.png $orig
convert windows/runner/resources/knot_64.png windows/runner/resources/knot_64.ico
inkscape -w 48 -h 48 -o windows/runner/resources/knot_48.png $orig
convert windows/runner/resources/knot_48.png windows/runner/resources/knot_48.ico
inkscape -w 32 -h 32 -o windows/runner/resources/knot_32.png $orig
convert windows/runner/resources/knot_32.png windows/runner/resources/knot_32.ico
inkscape -w 16 -h 16 -o windows/runner/resources/knot_16.png $orig
convert windows/runner/resources/knot_16.png windows/runner/resources/knot_16.ico
# android icons
inkscape -w 48 -h 48 -o android/app/src/main/res/mipmap-mdpi/knott_transparent.png $transparent
inkscape -w 48 -h 48 -o android/app/src/main/res/mipmap-mdpi/knott.png $orig
inkscape -w 72 -h 72 -o android/app/src/main/res/mipmap-hdpi/knott_transparent.png $transparent
inkscape -w 72 -h 72 -o android/app/src/main/res/mipmap-hdpi/knott.png $orig
inkscape -w 96 -h 96 -o android/app/src/main/res/mipmap-xhdpi/knott_transparent.png $transparent
inkscape -w 96 -h 96 -o android/app/src/main/res/mipmap-xhdpi/knott.png $orig
inkscape -w 144 -h 144 -o android/app/src/main/res/mipmap-xxhdpi/knott_transparent.png $transparent
inkscape -w 144 -h 144 -o android/app/src/main/res/mipmap-xxhdpi/knott.png $orig
inkscape -w 192 -h 192 -o android/app/src/main/res/mipmap-xxxhdpi/knott_transparent.png $transparent
inkscape -w 192 -h 192 -o android/app/src/main/res/mipmap-xxxhdpi/knott.png $orig

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 899 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB