forked from cwtch.im/cwtch-ui
Compare commits
5 Commits
trunk
...
flutter3_n
Author | SHA1 | Date |
---|---|---|
Dan Ballard | 85d21299ff | |
Dan Ballard | dabca870fb | |
Dan Ballard | c66e3583ab | |
Dan Ballard | 40f7613480 | |
Dan Ballard | af4fd22041 |
12
.drone.yml
12
.drone.yml
|
@ -8,7 +8,7 @@ clone:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: clone
|
- name: clone
|
||||||
image: cirrusci/flutter:2.8.0
|
image: cirrusci/flutter:3.0.1
|
||||||
environment:
|
environment:
|
||||||
buildbot_key_b64:
|
buildbot_key_b64:
|
||||||
from_secret: buildbot_key_b64
|
from_secret: buildbot_key_b64
|
||||||
|
@ -24,7 +24,7 @@ steps:
|
||||||
- git checkout $DRONE_COMMIT
|
- git checkout $DRONE_COMMIT
|
||||||
|
|
||||||
- name: fetch
|
- name: fetch
|
||||||
image: cirrusci/flutter:2.8.0
|
image: cirrusci/flutter:3.0.1
|
||||||
volumes:
|
volumes:
|
||||||
- name: deps
|
- name: deps
|
||||||
path: /root/.pub-cache
|
path: /root/.pub-cache
|
||||||
|
@ -47,7 +47,7 @@ steps:
|
||||||
# #Todo: fix all the lint errors and add `-set_exit_status` above to enforce linting
|
# #Todo: fix all the lint errors and add `-set_exit_status` above to enforce linting
|
||||||
|
|
||||||
- name: build-linux
|
- name: build-linux
|
||||||
image: openpriv/flutter-desktop:linux-fstable-2.8.0
|
image: openpriv/flutter-desktop:linux-fstable-3.0.1-rc1
|
||||||
volumes:
|
volumes:
|
||||||
- name: deps
|
- name: deps
|
||||||
path: /root/.pub-cache
|
path: /root/.pub-cache
|
||||||
|
@ -61,7 +61,7 @@ steps:
|
||||||
- rm -r cwtch
|
- rm -r cwtch
|
||||||
|
|
||||||
- name: test-build-android
|
- name: test-build-android
|
||||||
image: cirrusci/flutter:2.8.0
|
image: cirrusci/flutter:3.0.1
|
||||||
when:
|
when:
|
||||||
event: pull_request
|
event: pull_request
|
||||||
volumes:
|
volumes:
|
||||||
|
@ -71,7 +71,7 @@ steps:
|
||||||
- flutter build apk --debug
|
- flutter build apk --debug
|
||||||
|
|
||||||
- name: build-android
|
- name: build-android
|
||||||
image: cirrusci/flutter:2.8.0
|
image: cirrusci/flutter:3.0.1
|
||||||
when:
|
when:
|
||||||
event: push
|
event: push
|
||||||
environment:
|
environment:
|
||||||
|
@ -95,7 +95,7 @@ steps:
|
||||||
#- cp build/app/outputs/flutter-apk/app-debug.apk deploy/android
|
#- cp build/app/outputs/flutter-apk/app-debug.apk deploy/android
|
||||||
|
|
||||||
- name: widget-tests
|
- name: widget-tests
|
||||||
image: cirrusci/flutter:2.8.0
|
image: cirrusci/flutter:3.0.1
|
||||||
volumes:
|
volumes:
|
||||||
- name: deps
|
- name: deps
|
||||||
path: /root/.pub-cache
|
path: /root/.pub-cache
|
||||||
|
|
|
@ -40,17 +40,10 @@ app.*.symbols
|
||||||
# Obfuscation related
|
# Obfuscation related
|
||||||
app.*.map.json
|
app.*.map.json
|
||||||
|
|
||||||
# Tor
|
|
||||||
data-dir*
|
|
||||||
|
|
||||||
|
|
||||||
# Compiled Libs
|
|
||||||
linux/tor
|
linux/tor
|
||||||
linux/libCwtch.so
|
linux/libCwtch.so
|
||||||
android/cwtch/cwtch.aar
|
android/cwtch/cwtch.aar
|
||||||
android/app/src/main/jniLibs/*/libtor.so
|
android/app/src/main/jniLibs/*/libtor.so
|
||||||
libCwtch.dylib
|
|
||||||
|
|
||||||
coverage
|
coverage
|
||||||
test/failures
|
test/failures
|
||||||
.gradle
|
.gradle
|
||||||
|
|
|
@ -33,7 +33,7 @@ if (keystorePropertiesFile.exists()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
compileSdkVersion 31
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main.java.srcDirs += 'src/main/kotlin'
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
|
@ -48,7 +48,7 @@ android {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId "im.cwtch.flwtch"
|
applicationId "im.cwtch.flwtch"
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 30
|
targetSdkVersion 31
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
// Generated file.
|
// Generated file.
|
||||||
|
//
|
||||||
// If you wish to remove Flutter's multidex support, delete this entire file.
|
// If you wish to remove Flutter's multidex support, delete this entire file.
|
||||||
|
//
|
||||||
|
// Modifications to this file should be done in a copy under a different name
|
||||||
|
// as this file may be regenerated.
|
||||||
|
|
||||||
package io.flutter.app;
|
package io.flutter.app;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.annotation.CallSuper;
|
import androidx.annotation.CallSuper;
|
||||||
import androidx.multidex.MultiDex;
|
import androidx.multidex.MultiDex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension of {@link io.flutter.app.FlutterApplication}, adding multidex support.
|
* Extension of {@link android.app.Application}, adding multidex support.
|
||||||
*/
|
*/
|
||||||
public class FlutterMultiDexApplication extends FlutterApplication {
|
public class FlutterMultiDexApplication extends Application {
|
||||||
@Override
|
@Override
|
||||||
@CallSuper
|
@CallSuper
|
||||||
protected void attachBaseContext(Context base) {
|
protected void attachBaseContext(Context base) {
|
||||||
|
|
|
@ -536,10 +536,15 @@ class MainActivity: FlutterActivity() {
|
||||||
Log.i("MainActivity.kt", "onResume")
|
Log.i("MainActivity.kt", "onResume")
|
||||||
if (myReceiver == null) {
|
if (myReceiver == null) {
|
||||||
Log.i("MainActivity.kt", "onResume registering local broadcast receiver / event bus forwarder")
|
Log.i("MainActivity.kt", "onResume registering local broadcast receiver / event bus forwarder")
|
||||||
val mc = MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CWTCH_EVENTBUS)
|
val bm = flutterEngine?.dartExecutor?.binaryMessenger;
|
||||||
val filter = IntentFilter("im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS")
|
if (bm != null) {
|
||||||
myReceiver = MyBroadcastReceiver(mc)
|
val mc = MethodChannel(bm, CWTCH_EVENTBUS)
|
||||||
LocalBroadcastManager.getInstance(applicationContext).registerReceiver(myReceiver!!, filter)
|
|
||||||
|
val filter = IntentFilter("im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS")
|
||||||
|
myReceiver = MyBroadcastReceiver(mc)
|
||||||
|
LocalBroadcastManager.getInstance(applicationContext)
|
||||||
|
.registerReceiver(myReceiver!!, filter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReconnectCwtchForeground which will resync counters and settings...
|
// ReconnectCwtchForeground which will resync counters and settings...
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.3.50'
|
ext.kotlin_version = '1.5.31'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
// jCenter() no longer exists... https://blog.gradle.org/jcenter-shutdown
|
// jCenter() no longer exists... https://blog.gradle.org/jcenter-shutdown
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#Fri Jun 23 08:50:38 CEST 2017
|
#Mon Jun 20 10:33:21 PDT 2022
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
import 'package:cwtch/third_party/linkify/linkify.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|
||||||
|
|
||||||
void modalOpenLink(BuildContext ctx, LinkableElement link) {
|
|
||||||
showModalBottomSheet<void>(
|
|
||||||
context: ctx,
|
|
||||||
builder: (BuildContext bcontext) {
|
|
||||||
return Container(
|
|
||||||
height: 200, // bespoke value courtesy of the [TextField] docs
|
|
||||||
child: Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(30.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(AppLocalizations.of(bcontext)!.clickableLinksWarning),
|
|
||||||
Flex(direction: Axis.horizontal, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
|
|
||||||
child: ElevatedButton(
|
|
||||||
child: Text(AppLocalizations.of(bcontext)!.clickableLinksCopy, semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinksCopy),
|
|
||||||
onPressed: () {
|
|
||||||
Clipboard.setData(new ClipboardData(text: link.url));
|
|
||||||
|
|
||||||
final snackBar = SnackBar(
|
|
||||||
content: Text(AppLocalizations.of(bcontext)!.copiedToClipboardNotification),
|
|
||||||
);
|
|
||||||
|
|
||||||
Navigator.pop(bcontext);
|
|
||||||
ScaffoldMessenger.of(bcontext).showSnackBar(snackBar);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
|
|
||||||
child: ElevatedButton(
|
|
||||||
child: Text(AppLocalizations.of(bcontext)!.clickableLinkOpen, semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinkOpen),
|
|
||||||
onPressed: () async {
|
|
||||||
if (await canLaunch(link.url)) {
|
|
||||||
await launch(link.url);
|
|
||||||
Navigator.pop(bcontext);
|
|
||||||
} else {
|
|
||||||
final snackBar = SnackBar(
|
|
||||||
content: Text(AppLocalizations.of(bcontext)!.clickableLinkError),
|
|
||||||
);
|
|
||||||
ScaffoldMessenger.of(bcontext).showSnackBar(snackBar);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -312,6 +312,182 @@ class MaterialLocalizationLu extends MaterialLocalizations {
|
||||||
@override
|
@override
|
||||||
String get viewLicensesButtonLabel => 'LIZENZEN ANZEIGEN';
|
String get viewLicensesButtonLabel => 'LIZENZEN ANZEIGEN';
|
||||||
|
|
||||||
|
// ***** NEW *****
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyAlt => 'Alt';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyAltGraph => 'AltGr';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyBackspace => 'Backspace';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyCapsLock => 'Caps Lock';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyChannelDown => 'Kanal Erof';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyChannelUp => 'Kanal Up';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyControl => 'Ctrl';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyDelete => 'Del';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyEisu => 'Eisū';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyEject => 'Eject';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyEnd => 'End';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyEscape => 'Esc';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyFn => 'Fn';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyHangulMode => 'Hangul Mode';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyHanjaMode => 'Hanja Mode';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyHankaku => 'Hankaku';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyHiragana => 'Hiragana';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyHiraganaKatakana => 'Hiragana Katakana';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyHome => 'Home';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyInsert => 'Insert';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyKanaMode => 'Kana Mode';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyKanjiMode => 'Kanji Mode';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyKatakana => 'Katakana';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyMeta => 'Meta';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyMetaMacOs => 'Command';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyMetaWindows => 'Win';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumLock => 'Num Lock';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpad0 => 'Num 0';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpad1 => 'Num 1';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpad2 => 'Num 2';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpad3 => 'Num 3';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpad4 => 'Num 4';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpad5 => 'Num 5';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpad6 => 'Num 6';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpad7 => 'Num 7';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpad8 => 'Num 8';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpad9 => 'Num 9';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpadAdd => 'Num +';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpadComma => 'Num ,';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpadDecimal => 'Num .';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpadDivide => 'Num /';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpadEnter => 'Num Enter';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpadEqual => 'Num =';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpadMultiply => 'Num *';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpadParenLeft => 'Num (';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpadParenRight => 'Num )';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyNumpadSubtract => 'Num -';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyPageDown => 'PgDown';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyPageUp => 'PgUp';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyPower => 'Power';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyPowerOff => 'Power Off';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyPrintScreen => 'Print Screen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyRomaji => 'Romaji';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyScrollLock => 'Scroll Lock';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeySelect => 'Select';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeySpace => 'Spasie';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyZenkaku => 'Zenkaku';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get keyboardKeyZenkakuHankaku => 'Zenkaku Hankaku';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String aboutListTileTitle(String applicationName) {
|
String aboutListTileTitle(String applicationName) {
|
||||||
return aboutListTileTitleRaw.replaceFirst("$applicationName", applicationName);
|
return aboutListTileTitleRaw.replaceFirst("$applicationName", applicationName);
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
{
|
{
|
||||||
"@@locale": "cy",
|
"@@locale": "cy",
|
||||||
"@@last_modified": "2022-06-16T18:20:12+02:00",
|
"@@last_modified": "2022-04-21T21:35:58+02:00",
|
||||||
"tooltipPreviewFormatting": "Preview Message Formatting",
|
|
||||||
"tooltipCode": "Code \/ Monospace",
|
|
||||||
"tooltipStrikethrough": "Strikethrough",
|
|
||||||
"tooltipSubscript": "Subscript",
|
|
||||||
"tooltipSuperscript": "Superscript",
|
|
||||||
"tooltipItalicize": "Italic",
|
|
||||||
"tooltipBackToMessageEditing": "Back to Message Editing",
|
|
||||||
"tooltipBoldText": "Bold",
|
|
||||||
"formattingExperiment": "Fformatio Neges",
|
"formattingExperiment": "Fformatio Neges",
|
||||||
"clickableLinkOpen": "Agor URL",
|
"clickableLinkOpen": "Agor URL",
|
||||||
"clickableLinksCopy": "Copïo URL",
|
"clickableLinksCopy": "Copïo URL",
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
{
|
{
|
||||||
"@@locale": "da",
|
"@@locale": "da",
|
||||||
"@@last_modified": "2022-06-16T18:20:12+02:00",
|
"@@last_modified": "2022-04-21T21:35:58+02:00",
|
||||||
"tooltipPreviewFormatting": "Preview Message Formatting",
|
|
||||||
"tooltipCode": "Code \/ Monospace",
|
|
||||||
"tooltipStrikethrough": "Strikethrough",
|
|
||||||
"tooltipSubscript": "Subscript",
|
|
||||||
"tooltipSuperscript": "Superscript",
|
|
||||||
"tooltipItalicize": "Italic",
|
|
||||||
"tooltipBackToMessageEditing": "Back to Message Editing",
|
|
||||||
"tooltipBoldText": "Bold",
|
|
||||||
"okButton": "OK",
|
"okButton": "OK",
|
||||||
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
||||||
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
{
|
{
|
||||||
"@@locale": "de",
|
"@@locale": "de",
|
||||||
"@@last_modified": "2022-06-16T18:20:12+02:00",
|
"@@last_modified": "2022-04-21T21:35:58+02:00",
|
||||||
"tooltipPreviewFormatting": "Preview Message Formatting",
|
|
||||||
"tooltipCode": "Code \/ Monospace",
|
|
||||||
"tooltipStrikethrough": "Strikethrough",
|
|
||||||
"tooltipSubscript": "Subscript",
|
|
||||||
"tooltipSuperscript": "Superscript",
|
|
||||||
"tooltipItalicize": "Italic",
|
|
||||||
"tooltipBackToMessageEditing": "Back to Message Editing",
|
|
||||||
"tooltipBoldText": "Bold",
|
|
||||||
"okButton": "OK",
|
"okButton": "OK",
|
||||||
"settingsAndroidPowerReenablePopup": "Die Akku Optimierungen können nicht innerhalb von Cwtch wieder aktiviert werden. Bitte gehe zu Android \/ Einstellungen \/ Apps \/ Cwtch \/ Akku und setze die Akku Nutzung auf 'Optimiert'",
|
"settingsAndroidPowerReenablePopup": "Die Akku Optimierungen können nicht innerhalb von Cwtch wieder aktiviert werden. Bitte gehe zu Android \/ Einstellungen \/ Apps \/ Cwtch \/ Akku und setze die Akku Nutzung auf 'Optimiert'",
|
||||||
"settingAndroidPowerExemptionDescription": "Optional: Fordere Android auf, Cwtch von der optimierten Energieverwaltung auszunehmen. Dies wird zu einer besseren Stabilität auf Kosten eines höheren Batterieverbrauchs führen.",
|
"settingAndroidPowerExemptionDescription": "Optional: Fordere Android auf, Cwtch von der optimierten Energieverwaltung auszunehmen. Dies wird zu einer besseren Stabilität auf Kosten eines höheren Batterieverbrauchs führen.",
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
{
|
{
|
||||||
"@@locale": "el",
|
"@@locale": "el",
|
||||||
"@@last_modified": "2022-06-16T18:20:12+02:00",
|
"@@last_modified": "2022-04-21T21:35:58+02:00",
|
||||||
"tooltipPreviewFormatting": "Preview Message Formatting",
|
|
||||||
"tooltipCode": "Code \/ Monospace",
|
|
||||||
"tooltipStrikethrough": "Strikethrough",
|
|
||||||
"tooltipSubscript": "Subscript",
|
|
||||||
"tooltipSuperscript": "Superscript",
|
|
||||||
"tooltipItalicize": "Italic",
|
|
||||||
"tooltipBackToMessageEditing": "Back to Message Editing",
|
|
||||||
"tooltipBoldText": "Bold",
|
|
||||||
"okButton": "OK",
|
"okButton": "OK",
|
||||||
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
||||||
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
{
|
{
|
||||||
"@@locale": "en",
|
"@@locale": "en",
|
||||||
"@@last_modified": "2022-06-16T18:20:12+02:00",
|
"@@last_modified": "2022-04-21T21:35:58+02:00",
|
||||||
"tooltipPreviewFormatting": "Preview Message Formatting",
|
|
||||||
"tooltipCode": "Code \/ Monospace",
|
|
||||||
"tooltipStrikethrough": "Strikethrough",
|
|
||||||
"tooltipSubscript": "Subscript",
|
|
||||||
"tooltipSuperscript": "Superscript",
|
|
||||||
"tooltipItalicize": "Italic",
|
|
||||||
"tooltipBackToMessageEditing": "Back to Message Editing",
|
|
||||||
"tooltipBoldText": "Bold",
|
|
||||||
"okButton": "OK",
|
"okButton": "OK",
|
||||||
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
||||||
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
{
|
{
|
||||||
"@@locale": "es",
|
"@@locale": "es",
|
||||||
"@@last_modified": "2022-06-16T18:20:12+02:00",
|
"@@last_modified": "2022-04-21T21:35:58+02:00",
|
||||||
"tooltipPreviewFormatting": "Preview Message Formatting",
|
|
||||||
"tooltipCode": "Code \/ Monospace",
|
|
||||||
"tooltipStrikethrough": "Strikethrough",
|
|
||||||
"tooltipSubscript": "Subscript",
|
|
||||||
"tooltipSuperscript": "Superscript",
|
|
||||||
"tooltipItalicize": "Italic",
|
|
||||||
"tooltipBackToMessageEditing": "Back to Message Editing",
|
|
||||||
"tooltipBoldText": "Bold",
|
|
||||||
"okButton": "OK",
|
"okButton": "OK",
|
||||||
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
||||||
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
{
|
{
|
||||||
"@@locale": "fr",
|
"@@locale": "fr",
|
||||||
"@@last_modified": "2022-06-16T18:20:12+02:00",
|
"@@last_modified": "2022-04-21T21:35:58+02:00",
|
||||||
"tooltipPreviewFormatting": "Preview Message Formatting",
|
|
||||||
"tooltipCode": "Code \/ Monospace",
|
|
||||||
"tooltipStrikethrough": "Strikethrough",
|
|
||||||
"tooltipSubscript": "Subscript",
|
|
||||||
"tooltipSuperscript": "Superscript",
|
|
||||||
"tooltipItalicize": "Italic",
|
|
||||||
"tooltipBackToMessageEditing": "Back to Message Editing",
|
|
||||||
"tooltipBoldText": "Bold",
|
|
||||||
"acceptGroupBtn": "Accepter",
|
|
||||||
"settingAndroidPowerExemptionDescription": "Android applique par défaut un profil de gestion de l'énergie \"optimisé\" aux applications, ce qui peut entraîner leur arrêt ou leur suppression. Demandez à Android d'exempter Cwtch de ce profil pour une meilleure stabilité mais une plus grande consommation d'énergie.",
|
"settingAndroidPowerExemptionDescription": "Android applique par défaut un profil de gestion de l'énergie \"optimisé\" aux applications, ce qui peut entraîner leur arrêt ou leur suppression. Demandez à Android d'exempter Cwtch de ce profil pour une meilleure stabilité mais une plus grande consommation d'énergie.",
|
||||||
"settingsAndroidPowerReenablePopup": "Impossible de réactiver l'optimisation de la batterie à partir de Cwtch. Veuillez aller dans Android \/ Paramètres \/ Apps \/ Cwtch \/ Batterie et régler l'utilisation sur 'Optimisé'.",
|
"settingsAndroidPowerReenablePopup": "Impossible de réactiver l'optimisation de la batterie à partir de Cwtch. Veuillez aller dans Android \/ Paramètres \/ Apps \/ Cwtch \/ Batterie et régler l'utilisation sur 'Optimisé'.",
|
||||||
"okButton": "OK",
|
"okButton": "OK",
|
||||||
|
@ -21,6 +12,7 @@
|
||||||
"clickableLinksCopy": "Copier l'URL",
|
"clickableLinksCopy": "Copier l'URL",
|
||||||
"clickableLinkOpen": "Ouvrir l'URL",
|
"clickableLinkOpen": "Ouvrir l'URL",
|
||||||
"clickableLinkError": "Erreur rencontrée lors de la tentative d'ouverture de l'URL",
|
"clickableLinkError": "Erreur rencontrée lors de la tentative d'ouverture de l'URL",
|
||||||
|
"acceptGroupBtn": "Accepter",
|
||||||
"successfullyImportedProfile": "Profil importé avec succès : %profile",
|
"successfullyImportedProfile": "Profil importé avec succès : %profile",
|
||||||
"shuttingDownApp": "Fermeture...",
|
"shuttingDownApp": "Fermeture...",
|
||||||
"importProfileTooltip": "Utilisez une sauvegarde Cwtch chiffrée pour importer un profil créé dans une autre instance de Cwtch.",
|
"importProfileTooltip": "Utilisez une sauvegarde Cwtch chiffrée pour importer un profil créé dans une autre instance de Cwtch.",
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
{
|
{
|
||||||
"@@locale": "it",
|
"@@locale": "it",
|
||||||
"@@last_modified": "2022-06-16T18:20:12+02:00",
|
"@@last_modified": "2022-04-21T21:35:58+02:00",
|
||||||
"tooltipPreviewFormatting": "Preview Message Formatting",
|
|
||||||
"tooltipCode": "Code \/ Monospace",
|
|
||||||
"tooltipStrikethrough": "Strikethrough",
|
|
||||||
"tooltipSubscript": "Subscript",
|
|
||||||
"tooltipSuperscript": "Superscript",
|
|
||||||
"tooltipItalicize": "Italic",
|
|
||||||
"tooltipBackToMessageEditing": "Back to Message Editing",
|
|
||||||
"tooltipBoldText": "Bold",
|
|
||||||
"settingsAndroidPowerReenablePopup": "Impossibile riattivare l'ottimizzazione della batteria dall'interno di Cwtch. Vai su Android \/ Impostazioni \/ Apps \/ Cwtch \/ Informazioni App \/ (Utilizzo) Batteria e imposta su 'Ottimizzato'.",
|
"settingsAndroidPowerReenablePopup": "Impossibile riattivare l'ottimizzazione della batteria dall'interno di Cwtch. Vai su Android \/ Impostazioni \/ Apps \/ Cwtch \/ Informazioni App \/ (Utilizzo) Batteria e imposta su 'Ottimizzato'.",
|
||||||
"puzzleGameBtn": "Gioco di puzzle",
|
"puzzleGameBtn": "Gioco di puzzle",
|
||||||
"editProfileTitle": "Modifica il profilo",
|
"editProfileTitle": "Modifica il profilo",
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
{
|
{
|
||||||
"@@locale": "lb",
|
"@@locale": "lb",
|
||||||
"@@last_modified": "2022-06-16T18:20:12+02:00",
|
"@@last_modified": "2022-04-21T21:35:58+02:00",
|
||||||
"tooltipPreviewFormatting": "Preview Message Formatting",
|
|
||||||
"tooltipCode": "Code \/ Monospace",
|
|
||||||
"tooltipStrikethrough": "Strikethrough",
|
|
||||||
"tooltipSubscript": "Subscript",
|
|
||||||
"tooltipSuperscript": "Superscript",
|
|
||||||
"tooltipItalicize": "Italic",
|
|
||||||
"tooltipBackToMessageEditing": "Back to Message Editing",
|
|
||||||
"tooltipBoldText": "Bold",
|
|
||||||
"okButton": "OK",
|
"okButton": "OK",
|
||||||
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
||||||
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
{
|
{
|
||||||
"@@locale": "no",
|
"@@locale": "no",
|
||||||
"@@last_modified": "2022-06-16T18:20:12+02:00",
|
"@@last_modified": "2022-04-21T21:35:58+02:00",
|
||||||
"tooltipPreviewFormatting": "Preview Message Formatting",
|
|
||||||
"tooltipCode": "Code \/ Monospace",
|
|
||||||
"tooltipStrikethrough": "Strikethrough",
|
|
||||||
"tooltipSubscript": "Subscript",
|
|
||||||
"tooltipSuperscript": "Superscript",
|
|
||||||
"tooltipItalicize": "Italic",
|
|
||||||
"tooltipBackToMessageEditing": "Back to Message Editing",
|
|
||||||
"tooltipBoldText": "Bold",
|
|
||||||
"okButton": "OK",
|
"okButton": "OK",
|
||||||
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
||||||
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
{
|
{
|
||||||
"@@locale": "pl",
|
"@@locale": "pl",
|
||||||
"@@last_modified": "2022-06-16T18:20:12+02:00",
|
"@@last_modified": "2022-04-21T21:35:58+02:00",
|
||||||
"tooltipPreviewFormatting": "Preview Message Formatting",
|
|
||||||
"tooltipCode": "Code \/ Monospace",
|
|
||||||
"tooltipStrikethrough": "Strikethrough",
|
|
||||||
"tooltipSubscript": "Subscript",
|
|
||||||
"tooltipSuperscript": "Superscript",
|
|
||||||
"tooltipItalicize": "Italic",
|
|
||||||
"tooltipBackToMessageEditing": "Back to Message Editing",
|
|
||||||
"tooltipBoldText": "Bold",
|
|
||||||
"okButton": "OK",
|
"okButton": "OK",
|
||||||
"settingsAndroidPowerReenablePopup": "Nie udało się ponownie włączyć optymalizacji użycia baterii dla Cwtch. Przejdź do Android \/ Ustawienia \/ Aplikacje \/ Cwtch \/ Bateria i ustaw Zużycie na 'Optymalizacja'",
|
"settingsAndroidPowerReenablePopup": "Nie udało się ponownie włączyć optymalizacji użycia baterii dla Cwtch. Przejdź do Android \/ Ustawienia \/ Aplikacje \/ Cwtch \/ Bateria i ustaw Zużycie na 'Optymalizacja'",
|
||||||
"settingAndroidPowerExemptionDescription": "Opcjonalne: wyłącz optymalizację użycia baterii przez Cwtch w systemie Android. Będzie to skutkować lepszą stabilnością w zamian za wyższy pobór energii",
|
"settingAndroidPowerExemptionDescription": "Opcjonalne: wyłącz optymalizację użycia baterii przez Cwtch w systemie Android. Będzie to skutkować lepszą stabilnością w zamian za wyższy pobór energii",
|
||||||
|
@ -27,6 +19,12 @@
|
||||||
"importProfile": "Importuj profil",
|
"importProfile": "Importuj profil",
|
||||||
"exportProfileTooltip": "Utwórz zaszyfrowany plik z kopią zapasową tego profilu. Zaszyfrowany plik można zaimportować do Cwtch na innym urządzeniu.",
|
"exportProfileTooltip": "Utwórz zaszyfrowany plik z kopią zapasową tego profilu. Zaszyfrowany plik można zaimportować do Cwtch na innym urządzeniu.",
|
||||||
"exportProfile": "Eksportuj profil",
|
"exportProfile": "Eksportuj profil",
|
||||||
|
"deleteConfirmLabel": "Wpisz USUŃ aby potwierdzić",
|
||||||
|
"localeLb": "Luksemburski",
|
||||||
|
"localeNo": "Norweski",
|
||||||
|
"localeEl": "Grecki",
|
||||||
|
"localeCy": "Walijski",
|
||||||
|
"localeDa": "Duński",
|
||||||
"localeRo": "Romanian",
|
"localeRo": "Romanian",
|
||||||
"newMessageNotificationConversationInfo": "Nowa wiadomość od %1",
|
"newMessageNotificationConversationInfo": "Nowa wiadomość od %1",
|
||||||
"newMessageNotificationSimple": "Nowa wiadomość",
|
"newMessageNotificationSimple": "Nowa wiadomość",
|
||||||
|
@ -64,23 +62,6 @@
|
||||||
"torSettingsCustomSocksPort": "Port SOCKS",
|
"torSettingsCustomSocksPort": "Port SOCKS",
|
||||||
"torSettingsEnabledAdvancedDescription": "Użyj obecnego na twoim systemie serwisu Tor lub zmień parametry serwisu Tor w Cwtch",
|
"torSettingsEnabledAdvancedDescription": "Użyj obecnego na twoim systemie serwisu Tor lub zmień parametry serwisu Tor w Cwtch",
|
||||||
"torSettingsEnabledAdvanced": "Włącz zaawansowaną konfigurację Tor",
|
"torSettingsEnabledAdvanced": "Włącz zaawansowaną konfigurację Tor",
|
||||||
"archiveConversation": "Zarchiwizuj tę konwersację",
|
|
||||||
"groupInviteSettingsWarning": "Zaproszono Cię do grupy! Aby wyświetlić to zaproszenie, włącz Grupy (eksperymentalne) w Ustawieniach",
|
|
||||||
"descriptionExperimentsGroups": "Grupy (eksperymentalne) łączą się z niezaufanymi serwerami, aby umożliwić komunikację grupową.",
|
|
||||||
"descriptionExperiments": "Funkcje eksperymentalne są opcjonalne. Dodają one funkcjonalności, które mogą być mniej prywatne niż domyślne konwersacje 1:1, np. Grupy, integracja z botami, itp.",
|
|
||||||
"invalidImportString": "Niepoprawny ciąg importu",
|
|
||||||
"enableGroups": "Włącz Grupy",
|
|
||||||
"todoPlaceholder": "Do zrobienia...",
|
|
||||||
"profileOnionLabel": "Send this address to contacts you want to connect with",
|
|
||||||
"copiedToClipboardNotification": "Skopiowano do schowka",
|
|
||||||
"searchList": "Lista wyszukiwania",
|
|
||||||
"serverLabel": "Server",
|
|
||||||
"deleteConfirmLabel": "Wpisz USUŃ aby potwierdzić",
|
|
||||||
"localeLb": "Luksemburski",
|
|
||||||
"localeNo": "Norweski",
|
|
||||||
"localeEl": "Grecki",
|
|
||||||
"localeCy": "Walijski",
|
|
||||||
"localeDa": "Duński",
|
|
||||||
"largeTextLabel": "Duży",
|
"largeTextLabel": "Duży",
|
||||||
"settingInterfaceZoom": "Przybliżenie",
|
"settingInterfaceZoom": "Przybliżenie",
|
||||||
"localeDe": "Deutsche",
|
"localeDe": "Deutsche",
|
||||||
|
@ -111,6 +92,7 @@
|
||||||
"password1Label": "Hasło",
|
"password1Label": "Hasło",
|
||||||
"currentPasswordLabel": "Obecne hasło",
|
"currentPasswordLabel": "Obecne hasło",
|
||||||
"yourDisplayName": "Nazwa",
|
"yourDisplayName": "Nazwa",
|
||||||
|
"profileOnionLabel": "Przekaż ten adres osobom, z którymi chcesz nawiązać kontakt",
|
||||||
"noPasswordWarning": "Brak hasła do konta oznacza, że dane przechowywane na tym urządzeniu nie będą zaszyfrowane",
|
"noPasswordWarning": "Brak hasła do konta oznacza, że dane przechowywane na tym urządzeniu nie będą zaszyfrowane",
|
||||||
"radioNoPassword": "Niezaszyfrowany (brak hasła)",
|
"radioNoPassword": "Niezaszyfrowany (brak hasła)",
|
||||||
"radioUsePassword": "Hasło",
|
"radioUsePassword": "Hasło",
|
||||||
|
@ -120,13 +102,13 @@
|
||||||
"profileName": "Nazwa",
|
"profileName": "Nazwa",
|
||||||
"editProfileTitle": "Edytuj profil",
|
"editProfileTitle": "Edytuj profil",
|
||||||
"addProfileTitle": "Dodaj nowy profil",
|
"addProfileTitle": "Dodaj nowy profil",
|
||||||
"deleteBtn": "Delete",
|
"deleteBtn": "Usuń",
|
||||||
"unblockBtn": "Odblokuj",
|
"unblockBtn": "Odblokuj",
|
||||||
"dontSavePeerHistory": "Nie",
|
"dontSavePeerHistory": "Nie",
|
||||||
"savePeerHistoryDescription": "Zapisywanie wiadomości",
|
"savePeerHistoryDescription": "Zapisywanie wiadomości",
|
||||||
"savePeerHistory": "Tak",
|
"savePeerHistory": "Tak",
|
||||||
"blockBtn": "Zablokuj",
|
"blockBtn": "Zablokuj",
|
||||||
"saveBtn": "Save",
|
"saveBtn": "Zapisz",
|
||||||
"displayNameLabel": "Nazwa",
|
"displayNameLabel": "Nazwa",
|
||||||
"addressLabel": "Adresy",
|
"addressLabel": "Adresy",
|
||||||
"puzzleGameBtn": "Puzzle",
|
"puzzleGameBtn": "Puzzle",
|
||||||
|
@ -147,6 +129,7 @@
|
||||||
"membershipDescription": "Lista użytkowników, którzy wysyłali wiadomości w tej grupie. Członkowie grupy, którzy nie wysyłali żadnych wiadomości nie są na tej liście.",
|
"membershipDescription": "Lista użytkowników, którzy wysyłali wiadomości w tej grupie. Członkowie grupy, którzy nie wysyłali żadnych wiadomości nie są na tej liście.",
|
||||||
"addListItemBtn": "Dodaj",
|
"addListItemBtn": "Dodaj",
|
||||||
"peerNotOnline": "Znajomy jest niedostępny. Nie można użyć aplikacji.",
|
"peerNotOnline": "Znajomy jest niedostępny. Nie można użyć aplikacji.",
|
||||||
|
"searchList": "Lista wyszukiwania",
|
||||||
"update": "Zaktualizuj",
|
"update": "Zaktualizuj",
|
||||||
"inviteBtn": "Zaproś",
|
"inviteBtn": "Zaproś",
|
||||||
"inviteToGroupLabel": "Zaproś do grupy",
|
"inviteToGroupLabel": "Zaproś do grupy",
|
||||||
|
@ -158,6 +141,7 @@
|
||||||
"serverConnectivityConnected": "Połączono z serwerem",
|
"serverConnectivityConnected": "Połączono z serwerem",
|
||||||
"serverInfo": "Informacje o serwerze",
|
"serverInfo": "Informacje o serwerze",
|
||||||
"invitationLabel": "Zaproszenie",
|
"invitationLabel": "Zaproszenie",
|
||||||
|
"serverLabel": "Server",
|
||||||
"search": "Szukaj...",
|
"search": "Szukaj...",
|
||||||
"blocked": "Zablokowany",
|
"blocked": "Zablokowany",
|
||||||
"pasteAddressToAddContact": "Wklej adres Cwtch znajomego, zaproszenie do grupy albo pęk kluczy",
|
"pasteAddressToAddContact": "Wklej adres Cwtch znajomego, zaproszenie do grupy albo pęk kluczy",
|
||||||
|
@ -241,6 +225,7 @@
|
||||||
"tooltipRejectContactRequest": "Odrzuć zaproszenie do znajomych",
|
"tooltipRejectContactRequest": "Odrzuć zaproszenie do znajomych",
|
||||||
"tooltipAcceptContactRequest": "Akceptuj zaproszenie do znajomych",
|
"tooltipAcceptContactRequest": "Akceptuj zaproszenie do znajomych",
|
||||||
"notificationNewMessageFromPeer": "Nowa wiadomość od znajomego!",
|
"notificationNewMessageFromPeer": "Nowa wiadomość od znajomego!",
|
||||||
|
"groupInviteSettingsWarning": "Zaproszono Cię do grupy! Aby wyświetlić to zaproszenie, włącz Grupy (eksperymentalne) w Ustawieniach",
|
||||||
"shutdownCwtchDialog": "Zamknąć Cwtch? Wszystkie połączenia zostaną zakończone, a aplikacja zostanie zamknięta.",
|
"shutdownCwtchDialog": "Zamknąć Cwtch? Wszystkie połączenia zostaną zakończone, a aplikacja zostanie zamknięta.",
|
||||||
"malformedMessage": "Wiadomość uszkodzona",
|
"malformedMessage": "Wiadomość uszkodzona",
|
||||||
"profileDeleteSuccess": "Profil został usunięty",
|
"profileDeleteSuccess": "Profil został usunięty",
|
||||||
|
@ -263,16 +248,21 @@
|
||||||
"inviteToGroup": "Zaproszono Cię do grupy:",
|
"inviteToGroup": "Zaproszono Cię do grupy:",
|
||||||
"successfullAddedContact": "Dodano znajomego ",
|
"successfullAddedContact": "Dodano znajomego ",
|
||||||
"descriptionBlockUnknownConnections": "Blokowanie połączeń od osób, które nie są na liście Twoich znajomych.",
|
"descriptionBlockUnknownConnections": "Blokowanie połączeń od osób, które nie są na liście Twoich znajomych.",
|
||||||
|
"descriptionExperimentsGroups": "Grupy (eksperymentalne) łączą się z niezaufanymi serwerami, aby umożliwić komunikację grupową.",
|
||||||
|
"descriptionExperiments": "Funkcje eksperymentalne są opcjonalne. Dodają one funkcjonalności, które mogą być mniej prywatne niż domyślne konwersacje 1:1, np. Grupy, integracja z botami, itp.",
|
||||||
"titleManageProfiles": "Zarządzaj Profilami",
|
"titleManageProfiles": "Zarządzaj Profilami",
|
||||||
"tooltipUnlockProfiles": "Wprowadź hasło, aby odblokować zaszyfrowane profile.",
|
"tooltipUnlockProfiles": "Wprowadź hasło, aby odblokować zaszyfrowane profile.",
|
||||||
"titleManageContacts": "Konwersacje",
|
"titleManageContacts": "Konwersacje",
|
||||||
"tooltipAddContact": "Dodaj znajomego lub grupę",
|
"tooltipAddContact": "Dodaj znajomego lub grupę",
|
||||||
"tooltipOpenSettings": "Ustawienia",
|
"tooltipOpenSettings": "Ustawienia",
|
||||||
"contactAlreadyExists": "Ten znajomy już istnieje",
|
"contactAlreadyExists": "Ten znajomy już istnieje",
|
||||||
|
"invalidImportString": "Niepoprawny ciąg importu",
|
||||||
"conversationSettings": "Ustawienia konwersacji",
|
"conversationSettings": "Ustawienia konwersacji",
|
||||||
"enterCurrentPasswordForDelete": "Aby usunąć ten profil, wprowadź hasło.",
|
"enterCurrentPasswordForDelete": "Aby usunąć ten profil, wprowadź hasło.",
|
||||||
|
"enableGroups": "Włącz Grupy",
|
||||||
"localeIt": "Italiana",
|
"localeIt": "Italiana",
|
||||||
"localeEs": "Espanol",
|
"localeEs": "Espanol",
|
||||||
|
"todoPlaceholder": "Do zrobienia...",
|
||||||
"addNewItem": "Dodaj do listy",
|
"addNewItem": "Dodaj do listy",
|
||||||
"addListItem": "Add a New List Item",
|
"addListItem": "Add a New List Item",
|
||||||
"newConnectionPaneTitle": "Nowe połączenie",
|
"newConnectionPaneTitle": "Nowe połączenie",
|
||||||
|
@ -331,6 +321,7 @@
|
||||||
"fileSavedTo": "Zapisano do",
|
"fileSavedTo": "Zapisano do",
|
||||||
"verfiyResumeButton": "Zweryfikuj\/wznów",
|
"verfiyResumeButton": "Zweryfikuj\/wznów",
|
||||||
"copyServerKeys": "Kopiuj klucze",
|
"copyServerKeys": "Kopiuj klucze",
|
||||||
|
"archiveConversation": "Zarchiwizuj tę konwersację",
|
||||||
"streamerModeLabel": "Tryb streamera\/prezentacji",
|
"streamerModeLabel": "Tryb streamera\/prezentacji",
|
||||||
"retrievingManifestMessage": "Pobieranie informacji o pliku...",
|
"retrievingManifestMessage": "Pobieranie informacji o pliku...",
|
||||||
"openFolderButton": "Otwórz folder",
|
"openFolderButton": "Otwórz folder",
|
||||||
|
@ -339,5 +330,6 @@
|
||||||
"labelFilesize": "Rozmiar",
|
"labelFilesize": "Rozmiar",
|
||||||
"messageFileSent": "Plik został wysłany",
|
"messageFileSent": "Plik został wysłany",
|
||||||
"tooltipSendFile": "Wyślij plik",
|
"tooltipSendFile": "Wyślij plik",
|
||||||
"settingFileSharing": "Udostępnianie plików"
|
"settingFileSharing": "Udostępnianie plików",
|
||||||
|
"copiedToClipboardNotification": "Skopiowano do schowka"
|
||||||
}
|
}
|
|
@ -1,14 +1,6 @@
|
||||||
{
|
{
|
||||||
"@@locale": "pt",
|
"@@locale": "pt",
|
||||||
"@@last_modified": "2022-06-16T18:20:12+02:00",
|
"@@last_modified": "2022-04-21T21:35:58+02:00",
|
||||||
"tooltipPreviewFormatting": "Preview Message Formatting",
|
|
||||||
"tooltipCode": "Code \/ Monospace",
|
|
||||||
"tooltipStrikethrough": "Strikethrough",
|
|
||||||
"tooltipSubscript": "Subscript",
|
|
||||||
"tooltipSuperscript": "Superscript",
|
|
||||||
"tooltipItalicize": "Italic",
|
|
||||||
"tooltipBackToMessageEditing": "Back to Message Editing",
|
|
||||||
"tooltipBoldText": "Bold",
|
|
||||||
"okButton": "OK",
|
"okButton": "OK",
|
||||||
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
||||||
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
{
|
{
|
||||||
"@@locale": "ro",
|
"@@locale": "ro",
|
||||||
"@@last_modified": "2022-06-16T18:20:12+02:00",
|
"@@last_modified": "2022-04-21T21:35:58+02:00",
|
||||||
"tooltipPreviewFormatting": "Preview Message Formatting",
|
|
||||||
"tooltipCode": "Code \/ Monospace",
|
|
||||||
"tooltipStrikethrough": "Strikethrough",
|
|
||||||
"tooltipSubscript": "Subscript",
|
|
||||||
"tooltipSuperscript": "Superscript",
|
|
||||||
"tooltipItalicize": "Italic",
|
|
||||||
"tooltipBackToMessageEditing": "Back to Message Editing",
|
|
||||||
"tooltipBoldText": "Bold",
|
|
||||||
"okButton": "OK",
|
"okButton": "OK",
|
||||||
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
||||||
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
||||||
|
|
|
@ -1,84 +1,24 @@
|
||||||
{
|
{
|
||||||
"@@locale": "ru",
|
"@@locale": "ru",
|
||||||
"@@last_modified": "2022-06-16T18:20:12+02:00",
|
"@@last_modified": "2022-04-21T21:35:58+02:00",
|
||||||
"tooltipPreviewFormatting": "Preview Message Formatting",
|
|
||||||
"tooltipCode": "Code \/ Monospace",
|
|
||||||
"tooltipStrikethrough": "Strikethrough",
|
|
||||||
"tooltipSubscript": "Subscript",
|
|
||||||
"tooltipSuperscript": "Superscript",
|
|
||||||
"tooltipItalicize": "Italic",
|
|
||||||
"tooltipBackToMessageEditing": "Back to Message Editing",
|
|
||||||
"tooltipBoldText": "Bold",
|
|
||||||
"editProfile": "Изменить профиль",
|
|
||||||
"okButton": "OK",
|
"okButton": "OK",
|
||||||
"settingsAndroidPowerReenablePopup": "Невозможно перезапустить функцию оптимазации батарее для Cwtch. Перейдите в настройки Android \/ Настройки \/ Приложения и уведомления \/ Все приложения \/ Cwtch \/ Батарея \/ Эконоимя заряда \/ Отключена",
|
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
|
||||||
"settingAndroidPowerExemptionDescription": "Необязательно: в настройках Android исключите Cwtch в параметрах оптимизации батареи. Это улучшит стабильность за счёт небольшого расхода батареи..",
|
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
|
||||||
"settingAndroidPowerExemption": "Игнорировать оптимазацию батареи Android",
|
"settingAndroidPowerExemption": "Android Ignore Battery Optimizations",
|
||||||
"thisFeatureRequiresGroupExpermientsToBeEnabled": "Чтобы использовать данную функцию, в настройках необходимо включить \"Эксперементы\", затем \"Групповые чаты\"",
|
"thisFeatureRequiresGroupExpermientsToBeEnabled": "This feature requires the Groups Experiment to be enabled in Settings",
|
||||||
"messageFormattingDescription": "Включите форматирование, если к примеру хотите использовать **жирный-текст** и *курсив*",
|
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
|
||||||
"formattingExperiment": "Форматирование сообщений",
|
"formattingExperiment": "Message Formatting",
|
||||||
"clickableLinkError": "Ошибка при попытке открыть данную ссылку",
|
"clickableLinkError": "Error encountered while attempting to open URL",
|
||||||
"clickableLinksCopy": "Копировать ссылку",
|
"clickableLinksCopy": "Copy URL",
|
||||||
"clickableLinkOpen": "Открыть ссылку",
|
"clickableLinkOpen": "Open URL",
|
||||||
"clickableLinksWarning": "Открытие данной ссылки приведет к запуску приложения за пределами Cwtch и может раскрыть метаданные или иным образом поставить под угрозу безопасность Cwtch. Открывайте ссылки только от тех людей, которым вы доверяете. Вы уверены, что хотите продолжить?",
|
"clickableLinksWarning": "Opening this URL will launch an application outside of Cwtch and may reveal metadata or otherwise compromise the security of Cwtch. Only open URLs from people you trust. Are you sure you want to continue?",
|
||||||
"shuttingDownApp": "Выключение...",
|
"shuttingDownApp": "Shutting down...",
|
||||||
"successfullyImportedProfile": "Профиль успешно импортирован: %profile",
|
"successfullyImportedProfile": "Successfully Imported Profile: %profile",
|
||||||
"failedToImportProfile": "Ошибка импорта профиля",
|
"failedToImportProfile": "Error Importing Profile",
|
||||||
"importProfileTooltip": "Используйте зашифрованную резервную копию Cwtch, чтобы перенести профиль, созданный на другом устройстве где установлен Cwtch..",
|
"importProfileTooltip": "Use an encrypted Cwtch backup to bring in a profile created in another instance of Cwtch.",
|
||||||
"importProfile": "Загрузить профиль",
|
"importProfile": "Import Profile",
|
||||||
"exportProfileTooltip": "Сделать зашифрованную резервную копию в файл. Его потом потом можно импортировать на других устройствах где установлен Cwtch.",
|
"exportProfileTooltip": "Backup this profile to an encrypted file. The encrypted file can be imported into another Cwtch app.",
|
||||||
"exportProfile": "Сохранить профиль",
|
"exportProfile": "Export Profile",
|
||||||
"notificationContentContactInfo": "Информация о разговоре",
|
|
||||||
"notificationContentSimpleEvent": "Обычное событие",
|
|
||||||
"conversationNotificationPolicySettingDescription": "Настройка уведомлений",
|
|
||||||
"conversationNotificationPolicySettingLabel": "Настройка уведомлений",
|
|
||||||
"settingsGroupAppearance": "Настройки отображения",
|
|
||||||
"notificationContentSettingDescription": "Управление уведомлениями в данной теме",
|
|
||||||
"notificationPolicySettingDescription": "Настройка уведомлений по-умолчанию",
|
|
||||||
"notificationContentSettingLabel": "Содержимое уведомления",
|
|
||||||
"notificationPolicySettingLabel": "Уведомления",
|
|
||||||
"conversationNotificationPolicyOptIn": "Включить",
|
|
||||||
"conversationNotificationPolicyDefault": "По-умолчанию",
|
|
||||||
"notificationPolicyDefaultAll": "Всё по-умолчанию",
|
|
||||||
"notificationPolicyOptIn": "Включить",
|
|
||||||
"notificationPolicyMute": "Без звука",
|
|
||||||
"tooltipSelectACustomProfileImage": "Сменить изображение профиля",
|
|
||||||
"torSettingsEnabledCacheDescription": "Кэшировать текущий загруженный узел Tor для повторного подключения при следующем запуске Cwtch. Это позволит Tor запускаться быстрее. Если этот параметр отключен, Cwtch будет очищать кэшированные данные при запуске.",
|
|
||||||
"torSettingsEnableCache": "Кешировать узлы Tor",
|
|
||||||
"descriptionACNCircuitInfo": "Подробная информация о соединении, который сеть анонимной связи использует для подключения к этому разговору.",
|
|
||||||
"labelACNCircuitInfo": "Информация о цепи ACN",
|
|
||||||
"fileSharingSettingsDownloadFolderTooltip": "Нажмите обзор чтобы выбрать другую папку по-умолчанию для загружаемых файлов.",
|
|
||||||
"fileSharingSettingsDownloadFolderDescription": "При включение функции автоматическое скачивание файлов (например картинок), необходимо указать папку для сохранения.",
|
|
||||||
"torSettingsErrorSettingPort": "Номер порта должен быть в диапазоне от 1 до 65535",
|
|
||||||
"torSettingsUseCustomTorServiceConfigurastionDescription": "Переопределение конфигурации Tor по умолчанию. Предупреждение: это может быть опасно. Если не знаете, что делаете, лучше не трогать!",
|
|
||||||
"torSettingsUseCustomTorServiceConfiguration": "Используйте пользовательскую конфигурацию службы Tor (torrc)",
|
|
||||||
"torSettingsCustomControlPortDescription": "Используйте настраиваемый порт для управления подключениями к прокси-серверу Tor.",
|
|
||||||
"torSettingsCustomControlPort": "Выберите контрольный порт",
|
|
||||||
"torSettingsCustomSocksPortDescription": "Используйте настраиваемый порт для подключения к прокси-серверу Tor.",
|
|
||||||
"torSettingsCustomSocksPort": "Выберите SOCKS порт",
|
|
||||||
"torSettingsEnabledAdvancedDescription": "Использовать установленную службу Tor в вашей системе или измените параметры службы Cwtch Tor",
|
|
||||||
"torSettingsEnabledAdvanced": "Включить расширенные настройки Tor",
|
|
||||||
"themeColorLabel": "Основной цвет темы",
|
|
||||||
"settingDownloadFolder": "Папка для загрузок",
|
|
||||||
"settingImagePreviewsDescription": "Автоматическая загрузка изображений. Обратите внимание, что предварительный просмотр изображений часто может использоваться для взлома или деаномизации. Не используйте данную функцию если Вы контактируете с ненадежными контактами.",
|
|
||||||
"importLocalServerLabel": "Использовать локальный сервер",
|
|
||||||
"deleteServerConfirmBtn": "Вы точно хотите удалить сервер?",
|
|
||||||
"unlockProfileTip": "Создайте или импортируйте профиль, чтобы начать",
|
|
||||||
"unlockServerTip": "Создайте или импортируйте сервер, чтобы начать",
|
|
||||||
"saveServerButton": "Сохранить",
|
|
||||||
"serverEnabled": "Состояние сервера",
|
|
||||||
"descriptionFileSharing": "Данная функция позволяет обмениваться файлами напрямую с контактами и группами в Cwtch. Отправляемый файл будет напрямую скачиваться с вашего устройства через Cwtch, поверх скрытой сети Tor",
|
|
||||||
"settingUIColumnOptionSame": "Как в портретном режиме",
|
|
||||||
"settingUIColumnSingle": "Один столбец",
|
|
||||||
"createProfileToBegin": "Пожалуйста, создайте или импортируйте профиль, чтобы начать",
|
|
||||||
"yesLeave": "Удалить",
|
|
||||||
"leaveConversation": "Удалить",
|
|
||||||
"enableGroups": "Групповые чаты",
|
|
||||||
"settingTheme": "Ночной режим",
|
|
||||||
"addNewProfileBtn": "Создать новый профиль",
|
|
||||||
"profileOnionLabel": "Send this address to contacts you want to connect with",
|
|
||||||
"savePeerHistoryDescription": "Определяет политику хранения или удаления сообщений с данным контактом",
|
|
||||||
"savePeerHistory": "Настройка истории",
|
|
||||||
"deleteConfirmLabel": "Введите УДАЛИТЬ, чтобы продолжить",
|
"deleteConfirmLabel": "Введите УДАЛИТЬ, чтобы продолжить",
|
||||||
"deleteConfirmText": "УДАЛИТЬ",
|
"deleteConfirmText": "УДАЛИТЬ",
|
||||||
"localeCy": "Валлийский",
|
"localeCy": "Валлийский",
|
||||||
|
@ -86,24 +26,49 @@
|
||||||
"localeEl": "Греческий",
|
"localeEl": "Греческий",
|
||||||
"localeNo": "Норвежский",
|
"localeNo": "Норвежский",
|
||||||
"localeLb": "Люксембургский",
|
"localeLb": "Люксембургский",
|
||||||
|
"settingsGroupAppearance": "Появление",
|
||||||
"settingGroupBehaviour": "Поведение",
|
"settingGroupBehaviour": "Поведение",
|
||||||
"settingsGroupExperiments": "Эксперименты",
|
"settingsGroupExperiments": "Эксперименты",
|
||||||
"labelTorNetwork": "Сеть Tor",
|
"labelTorNetwork": "Сеть Tor",
|
||||||
|
"notificationPolicyMute": "Тишина",
|
||||||
"conversationNotificationPolicyNever": "Никогда",
|
"conversationNotificationPolicyNever": "Никогда",
|
||||||
"newMessageNotificationConversationInfo": "Новое сообщение от %1",
|
"newMessageNotificationConversationInfo": "Новое сообщение от %1",
|
||||||
"newMessageNotificationSimple": "Новое сообщение",
|
"newMessageNotificationSimple": "Новое сообщение",
|
||||||
"localeRo": "Румынский",
|
"localeRo": "Румынский",
|
||||||
|
"notificationContentContactInfo": "Conversation Information",
|
||||||
|
"notificationContentSimpleEvent": "Plain Event",
|
||||||
|
"conversationNotificationPolicySettingDescription": "Control notification behaviour for this conversation",
|
||||||
|
"conversationNotificationPolicySettingLabel": "Conversation Notification Policy",
|
||||||
|
"notificationContentSettingDescription": "Controls the contents of conversation notifications",
|
||||||
|
"notificationPolicySettingDescription": "Controls the default application notification behaviour",
|
||||||
|
"notificationContentSettingLabel": "Notification Content",
|
||||||
|
"notificationPolicySettingLabel": "Notification Policy",
|
||||||
|
"conversationNotificationPolicyOptIn": "Opt In",
|
||||||
|
"conversationNotificationPolicyDefault": "Default",
|
||||||
|
"notificationPolicyDefaultAll": "Default All",
|
||||||
|
"notificationPolicyOptIn": "Opt In",
|
||||||
|
"tooltipSelectACustomProfileImage": "Select a Custom Profile Image",
|
||||||
|
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
|
||||||
|
"torSettingsEnableCache": "Cache Tor Consensus",
|
||||||
|
"descriptionACNCircuitInfo": "In depth information about the path that the anonymous communication network is using to connect to this conversation.",
|
||||||
|
"labelACNCircuitInfo": "ACN Circuit Info",
|
||||||
|
"fileSharingSettingsDownloadFolderTooltip": "Browse to select a different default folder for downloaded files.",
|
||||||
|
"fileSharingSettingsDownloadFolderDescription": "When files are downloaded automatically (e.g. image files, when image previews are enabled) a default location to download the files to is needed.",
|
||||||
|
"torSettingsErrorSettingPort": "Port Number must be between 1 and 65535",
|
||||||
"msgAddToAccept": "Добавьте учетную запись в контакты, чтобы принять этот файл.",
|
"msgAddToAccept": "Добавьте учетную запись в контакты, чтобы принять этот файл.",
|
||||||
"btnSendFile": "Отправить файл",
|
"btnSendFile": "Отправить файл",
|
||||||
"msgConfirmSend": "Вы уверены, что хотите отправить?",
|
"msgConfirmSend": "Вы уверены, что хотите отправить?",
|
||||||
"msgFileTooBig": "Размер файла не должен превышать 10GB",
|
"msgFileTooBig": "Размер файла не должен превышать 10GB",
|
||||||
"storageMigrationModalMessage": "Перенос профилей в новый формат хранения. Это может занять несколько минут...",
|
"storageMigrationModalMessage": "Перенос профилей в новый формат хранения. Это может занять несколько минут...",
|
||||||
"loadingCwtch": "Загрузка Cwtch...",
|
"loadingCwtch": "Загрузка Cwtch...",
|
||||||
|
"themeColorLabel": "Светлая или Темная тема",
|
||||||
|
"settingDownloadFolder": "Папка для скачивания",
|
||||||
"serverConnectionsLabel": "Всего соединений:",
|
"serverConnectionsLabel": "Всего соединений:",
|
||||||
"serverTotalMessagesLabel": "Всего сообщений:",
|
"serverTotalMessagesLabel": "Всего сообщений:",
|
||||||
"plainServerDescription": "Мы настоятельно рекомендуем защитить свой Onion сервер Cwtch паролем. Если Вы этого не сделаете, то любой у кого окажется доступ к серверу, сможет получить доступ к информации на этом сервере включая конфиденциальные криптографические ключи.",
|
"plainServerDescription": "Мы настоятельно рекомендуем защитить свой Onion сервер Cwtch паролем. Если Вы этого не сделаете, то любой у кого окажется доступ к серверу, сможет получить доступ к информации на этом сервере включая конфиденциальные криптографические ключи.",
|
||||||
"settingServersDescription": "Экспериментальная функция которая позволяет добавлять сервер для Cwtch в скрытой сети Tor. В меню появится дополнительная опция Серверы",
|
"settingServersDescription": "Экспериментальная функция которая позволяет добавлять сервер для Cwtch в скрытой сети Tor. В меню появится дополнительная опция Серверы",
|
||||||
"streamerModeLabel": "Режим маскировки",
|
"streamerModeLabel": "Режим маскировки",
|
||||||
|
"settingUIColumnSingle": "Один стобец",
|
||||||
"settingUIColumnLandscape": "Столбцы чатов в ландшафтном режиме",
|
"settingUIColumnLandscape": "Столбцы чатов в ландшафтном режиме",
|
||||||
"settingUIColumnPortrait": "Столбцы чатов в портретном режиме",
|
"settingUIColumnPortrait": "Столбцы чатов в портретном режиме",
|
||||||
"resetTor": "Сброс",
|
"resetTor": "Сброс",
|
||||||
|
@ -112,6 +77,14 @@
|
||||||
"descriptionExperiments": "Экспериментальные функции Cwtch это необязательные дополнительные функции, которые добавляют некоторые возможности, но не имеют такой же устойчивости к метаданным как если бы вы общались через традиционный чат 1 на 1..",
|
"descriptionExperiments": "Экспериментальные функции Cwtch это необязательные дополнительные функции, которые добавляют некоторые возможности, но не имеют такой же устойчивости к метаданным как если бы вы общались через традиционный чат 1 на 1..",
|
||||||
"settingLanguage": "Выбрать язык",
|
"settingLanguage": "Выбрать язык",
|
||||||
"profileName": "Введите имя...",
|
"profileName": "Введите имя...",
|
||||||
|
"torSettingsUseCustomTorServiceConfigurastionDescription": "Override the default tor configuration. Warning: This could be dangerous. Only turn this on if you know what you are doing.",
|
||||||
|
"torSettingsUseCustomTorServiceConfiguration": "Use a Custom Tor Service Configuration (torrc)",
|
||||||
|
"torSettingsCustomControlPortDescription": "Use a custom port for control connections to the Tor proxy",
|
||||||
|
"torSettingsCustomControlPort": "Custom Control Port",
|
||||||
|
"torSettingsCustomSocksPortDescription": "Use a custom port for data connections to the Tor proxy",
|
||||||
|
"torSettingsCustomSocksPort": "Custom SOCKS Port",
|
||||||
|
"torSettingsEnabledAdvancedDescription": "Use an existing Tor service on your system, or change the parameters of the Cwtch Tor Service",
|
||||||
|
"torSettingsEnabledAdvanced": "Enable Advanced Tor Configuration",
|
||||||
"themeNameNeon2": "Неон2",
|
"themeNameNeon2": "Неон2",
|
||||||
"themeNameNeon1": "Неон1",
|
"themeNameNeon1": "Неон1",
|
||||||
"themeNameMidnight": "Полночь",
|
"themeNameMidnight": "Полночь",
|
||||||
|
@ -121,6 +94,7 @@
|
||||||
"themeNameVampire": "Вампир",
|
"themeNameVampire": "Вампир",
|
||||||
"themeNameWitch": "Ведьма",
|
"themeNameWitch": "Ведьма",
|
||||||
"themeNameCwtch": "Cwtch",
|
"themeNameCwtch": "Cwtch",
|
||||||
|
"settingImagePreviewsDescription": "Автоматическая загрузка изображений. Обратите внимание, что предварительный просмотр изображений часто может использоваться для взлома или деаномизации. Не используйте данную функцию если Вы контактируете с ненадежными контактами. Аватары профиля запланированы для версии Cwtch 1.6.",
|
||||||
"settingImagePreviews": "Предпросмотр изображений и фотографий профиля",
|
"settingImagePreviews": "Предпросмотр изображений и фотографий профиля",
|
||||||
"experimentClickableLinksDescription": "Экспериментальная функция которая позволяет нажимать на URL адреса в сообщениях",
|
"experimentClickableLinksDescription": "Экспериментальная функция которая позволяет нажимать на URL адреса в сообщениях",
|
||||||
"enableExperimentClickableLinks": "Включить кликабельные ссылки",
|
"enableExperimentClickableLinks": "Включить кликабельные ссылки",
|
||||||
|
@ -133,8 +107,11 @@
|
||||||
"groupsOnThisServerLabel": "Группы, в которых я нахожусь, размещены на этом сервере",
|
"groupsOnThisServerLabel": "Группы, в которых я нахожусь, размещены на этом сервере",
|
||||||
"importLocalServerButton": "Импорт %1",
|
"importLocalServerButton": "Импорт %1",
|
||||||
"importLocalServerSelectText": "Выбрать локальный сервер",
|
"importLocalServerSelectText": "Выбрать локальный сервер",
|
||||||
|
"importLocalServerLabel": "Импортировать локальный сервер",
|
||||||
"newMessagesLabel": "Новое сообщение",
|
"newMessagesLabel": "Новое сообщение",
|
||||||
"localeRU": "Русский",
|
"localeRU": "Русский",
|
||||||
|
"profileOnionLabel": "Send this address to contacts you want to connect with",
|
||||||
|
"savePeerHistory": "Хранить историю",
|
||||||
"saveBtn": "Сохранить",
|
"saveBtn": "Сохранить",
|
||||||
"networkStatusOnline": "В сети",
|
"networkStatusOnline": "В сети",
|
||||||
"defaultProfileName": "Алиса",
|
"defaultProfileName": "Алиса",
|
||||||
|
@ -150,23 +127,29 @@
|
||||||
"fileInterrupted": "Прервано",
|
"fileInterrupted": "Прервано",
|
||||||
"fileSavedTo": "Сохранить в",
|
"fileSavedTo": "Сохранить в",
|
||||||
"encryptedServerDescription": "Шифрование сервера паролем защитит его от других людей у которых может оказаться доступ к этому устройству, включая Onion адрес сервера. Зашифрованный сервер нельзя расшифровать, пока не будет введен правильный пароль разблокировки.",
|
"encryptedServerDescription": "Шифрование сервера паролем защитит его от других людей у которых может оказаться доступ к этому устройству, включая Onion адрес сервера. Зашифрованный сервер нельзя расшифровать, пока не будет введен правильный пароль разблокировки.",
|
||||||
|
"deleteServerConfirmBtn": "Точно удалить сервер?",
|
||||||
"deleteServerSuccess": "Сервер успешно удален",
|
"deleteServerSuccess": "Сервер успешно удален",
|
||||||
"enterCurrentPasswordForDeleteServer": "Пожалуйста, введите пароль сервера, чтобы удалить его",
|
"enterCurrentPasswordForDeleteServer": "Пожалуйста, введите пароль сервера, чтобы удалить его",
|
||||||
"copyAddress": "Копировать адрес",
|
"copyAddress": "Копировать адрес",
|
||||||
"settingServers": "Использовать серверы",
|
"settingServers": "Использовать серверы",
|
||||||
"enterServerPassword": "Введите пароль для разблокировки сервера",
|
"enterServerPassword": "Введите пароль для разблокировки сервера",
|
||||||
|
"unlockProfileTip": "Создайте или разблокируйте профиль, чтобы начать",
|
||||||
|
"unlockServerTip": "Создайте или разблокируйте сервер, чтобы начать",
|
||||||
"addServerTooltip": "Добавить сервер",
|
"addServerTooltip": "Добавить сервер",
|
||||||
"serversManagerTitleShort": "Серверы",
|
"serversManagerTitleShort": "Серверы",
|
||||||
"serversManagerTitleLong": "Личные серверы",
|
"serversManagerTitleLong": "Личные серверы",
|
||||||
|
"saveServerButton": "Сохранить сервер",
|
||||||
"serverAutostartDescription": "Автозапуск сервера при старте программы",
|
"serverAutostartDescription": "Автозапуск сервера при старте программы",
|
||||||
"serverAutostartLabel": "Автозапуск",
|
"serverAutostartLabel": "Автозапуск",
|
||||||
"serverEnabledDescription": "Запустить или остановить сервер",
|
"serverEnabledDescription": "Запустить или остановить сервер",
|
||||||
|
"serverEnabled": "Сервер запущен",
|
||||||
"serverDescriptionDescription": "Описание видите только Вы. Сделано для удобства",
|
"serverDescriptionDescription": "Описание видите только Вы. Сделано для удобства",
|
||||||
"serverDescriptionLabel": "Описание сервера",
|
"serverDescriptionLabel": "Описание сервера",
|
||||||
"serverAddress": "Адрес сервера",
|
"serverAddress": "Адрес сервера",
|
||||||
"editServerTitle": "Изменить сервер",
|
"editServerTitle": "Изменить сервер",
|
||||||
"addServerTitle": "Добавить сервер",
|
"addServerTitle": "Добавить сервер",
|
||||||
"titleManageProfilesShort": "Профили",
|
"titleManageProfilesShort": "Профили",
|
||||||
|
"descriptionFileSharing": "Данная функция позволяет обмениваться файлами напрямую с контактами и группами в Cwtch. Отправляемый файл будет напрямую скачиваться с вашего устройства через Cwtch.",
|
||||||
"settingFileSharing": "Передача файлов",
|
"settingFileSharing": "Передача файлов",
|
||||||
"tooltipSendFile": "Отправить файл",
|
"tooltipSendFile": "Отправить файл",
|
||||||
"messageFileOffered": "Контакт предлагает загрузить вам файл",
|
"messageFileOffered": "Контакт предлагает загрузить вам файл",
|
||||||
|
@ -188,6 +171,7 @@
|
||||||
"addContactConfirm": "Добавить контакт %1",
|
"addContactConfirm": "Добавить контакт %1",
|
||||||
"addContact": "Добавить контакт",
|
"addContact": "Добавить контакт",
|
||||||
"contactGoto": "Перейти к сообщению от %1",
|
"contactGoto": "Перейти к сообщению от %1",
|
||||||
|
"settingUIColumnOptionSame": "Как в настройках портретного режима",
|
||||||
"settingUIColumnDouble14Ratio": "Двойной (1:4)",
|
"settingUIColumnDouble14Ratio": "Двойной (1:4)",
|
||||||
"settingUIColumnDouble12Ratio": "Двойной (1:2)",
|
"settingUIColumnDouble12Ratio": "Двойной (1:2)",
|
||||||
"localePl": "Польский",
|
"localePl": "Польский",
|
||||||
|
@ -209,6 +193,7 @@
|
||||||
"debugLog": "Влючить отладку через консоль",
|
"debugLog": "Влючить отладку через консоль",
|
||||||
"torNetworkStatus": "Статус сети Tor",
|
"torNetworkStatus": "Статус сети Tor",
|
||||||
"addContactFirst": "Добавьте или выберите контакт, чтобы начать чат.",
|
"addContactFirst": "Добавьте или выберите контакт, чтобы начать чат.",
|
||||||
|
"createProfileToBegin": "Пожалуйста, создайте или разблокируйте профиль, чтобы начать",
|
||||||
"nickChangeSuccess": "Имя профиля успешно изменено",
|
"nickChangeSuccess": "Имя профиля успешно изменено",
|
||||||
"addServerFirst": "Перед созданием группы, необходимо создать сервер",
|
"addServerFirst": "Перед созданием группы, необходимо создать сервер",
|
||||||
"deleteProfileSuccess": "Профиль успешно удален",
|
"deleteProfileSuccess": "Профиль успешно удален",
|
||||||
|
@ -223,7 +208,9 @@
|
||||||
"accepted": "Принять!",
|
"accepted": "Принять!",
|
||||||
"chatHistoryDefault": "Этот чат будет удален после закрытия Cwtch! Историю сообщений можно включить для каждого чата отдельно через меню настроек в правом верхнем углу..",
|
"chatHistoryDefault": "Этот чат будет удален после закрытия Cwtch! Историю сообщений можно включить для каждого чата отдельно через меню настроек в правом верхнем углу..",
|
||||||
"newPassword": "Новый пароль",
|
"newPassword": "Новый пароль",
|
||||||
|
"yesLeave": "Да, оставить этот чат",
|
||||||
"reallyLeaveThisGroupPrompt": "Вы уверены, что хотите закончить этот разговор? Все сообщения будут удалены.",
|
"reallyLeaveThisGroupPrompt": "Вы уверены, что хотите закончить этот разговор? Все сообщения будут удалены.",
|
||||||
|
"leaveConversation": "Да, оставить этот чат",
|
||||||
"inviteToGroup": "Вас пригласили присоединиться к группе:",
|
"inviteToGroup": "Вас пригласили присоединиться к группе:",
|
||||||
"titleManageServers": "Управление серверами",
|
"titleManageServers": "Управление серверами",
|
||||||
"successfullAddedContact": "Успешно добавлен",
|
"successfullAddedContact": "Успешно добавлен",
|
||||||
|
@ -236,6 +223,7 @@
|
||||||
"invalidImportString": "Недействительная строка импорта",
|
"invalidImportString": "Недействительная строка импорта",
|
||||||
"conversationSettings": "Настройки чата",
|
"conversationSettings": "Настройки чата",
|
||||||
"enterCurrentPasswordForDelete": "Пожалуйста, введите текущий пароль, чтобы удалить этот профиль.",
|
"enterCurrentPasswordForDelete": "Пожалуйста, введите текущий пароль, чтобы удалить этот профиль.",
|
||||||
|
"enableGroups": "Включить Групповые чаты",
|
||||||
"localeIt": "Итальянский",
|
"localeIt": "Итальянский",
|
||||||
"localeEs": "Испанский",
|
"localeEs": "Испанский",
|
||||||
"todoPlaceholder": "Выполняю...",
|
"todoPlaceholder": "Выполняю...",
|
||||||
|
@ -255,6 +243,7 @@
|
||||||
"experimentsEnabled": "Включить Экспериментальные функции",
|
"experimentsEnabled": "Включить Экспериментальные функции",
|
||||||
"themeDark": "Темная",
|
"themeDark": "Темная",
|
||||||
"themeLight": "Светлая",
|
"themeLight": "Светлая",
|
||||||
|
"settingTheme": "Тема",
|
||||||
"largeTextLabel": "Большой",
|
"largeTextLabel": "Большой",
|
||||||
"settingInterfaceZoom": "Уровень масштабирования",
|
"settingInterfaceZoom": "Уровень масштабирования",
|
||||||
"localeDe": "Немецкий",
|
"localeDe": "Немецкий",
|
||||||
|
@ -271,6 +260,7 @@
|
||||||
"error0ProfilesLoadedForPassword": "0 профилей, загруженных с этим паролем",
|
"error0ProfilesLoadedForPassword": "0 профилей, загруженных с этим паролем",
|
||||||
"password": "Пароль",
|
"password": "Пароль",
|
||||||
"enterProfilePassword": "Введите пароль для просмотра ваших профилей",
|
"enterProfilePassword": "Введите пароль для просмотра ваших профилей",
|
||||||
|
"addNewProfileBtn": "Добавить новый профиль",
|
||||||
"deleteProfileConfirmBtn": "Действительно удалить профиль?",
|
"deleteProfileConfirmBtn": "Действительно удалить профиль?",
|
||||||
"deleteProfileBtn": "Удалить профиль",
|
"deleteProfileBtn": "Удалить профиль",
|
||||||
"passwordChangeError": "Ошибка при смене пароля: Введенный пароль отклонен",
|
"passwordChangeError": "Ошибка при смене пароля: Введенный пароль отклонен",
|
||||||
|
@ -285,11 +275,13 @@
|
||||||
"noPasswordWarning": "Отсутствие пароля в этой учетной записи означает, что все данные, хранящиеся локально, не будут зашифрованы",
|
"noPasswordWarning": "Отсутствие пароля в этой учетной записи означает, что все данные, хранящиеся локально, не будут зашифрованы",
|
||||||
"radioNoPassword": "Незашифрованный (без пароля)",
|
"radioNoPassword": "Незашифрованный (без пароля)",
|
||||||
"radioUsePassword": "Пароль",
|
"radioUsePassword": "Пароль",
|
||||||
|
"editProfile": "Изменить профиль",
|
||||||
"newProfile": "Новый профиль",
|
"newProfile": "Новый профиль",
|
||||||
"editProfileTitle": "Изменить профиль",
|
"editProfileTitle": "Изменить профиль",
|
||||||
"addProfileTitle": "Добавить новый профиль",
|
"addProfileTitle": "Добавить новый профиль",
|
||||||
"unblockBtn": "Разблокировать контакт",
|
"unblockBtn": "Разблокировать контакт",
|
||||||
"dontSavePeerHistory": "Удалить историю",
|
"dontSavePeerHistory": "Удалить историю",
|
||||||
|
"savePeerHistoryDescription": "Определяет политуку хранения или удаления переписки с данным контактом.",
|
||||||
"blockBtn": "Заблокировать контакт",
|
"blockBtn": "Заблокировать контакт",
|
||||||
"displayNameLabel": "Отображаемое имя",
|
"displayNameLabel": "Отображаемое имя",
|
||||||
"addressLabel": "Адрес",
|
"addressLabel": "Адрес",
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import 'package:cwtch/widgets/messagerow.dart';
|
import 'package:cwtch/widgets/messagerow.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
|
||||||
|
|
||||||
import 'message.dart';
|
import 'message.dart';
|
||||||
import 'messagecache.dart';
|
import 'messagecache.dart';
|
||||||
|
@ -45,7 +44,6 @@ class ContactInfoState extends ChangeNotifier {
|
||||||
late Map<String, GlobalKey<MessageRowState>> keys;
|
late Map<String, GlobalKey<MessageRowState>> keys;
|
||||||
int _newMarkerMsgIndex = -1;
|
int _newMarkerMsgIndex = -1;
|
||||||
late MessageCache messageCache;
|
late MessageCache messageCache;
|
||||||
ItemScrollController messageScrollController = new ItemScrollController();
|
|
||||||
|
|
||||||
// todo: a nicer way to model contacts, groups and other "entities"
|
// todo: a nicer way to model contacts, groups and other "entities"
|
||||||
late bool _isGroup;
|
late bool _isGroup;
|
||||||
|
|
|
@ -21,7 +21,7 @@ class ContactListState extends ChangeNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ContactInfoState> filteredList() {
|
List<ContactInfoState> filteredList() {
|
||||||
if (!isFiltered) return _contacts;
|
if (!isFiltered) return contacts;
|
||||||
return _contacts.where((ContactInfoState c) => c.onion.toLowerCase().startsWith(_filter) || (c.nickname.toLowerCase().contains(_filter))).toList();
|
return _contacts.where((ContactInfoState c) => c.onion.toLowerCase().startsWith(_filter) || (c.nickname.toLowerCase().contains(_filter))).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,8 +64,6 @@ class FileMessage extends Message {
|
||||||
}
|
}
|
||||||
return Container(
|
return Container(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
width: 50,
|
|
||||||
height: 50,
|
|
||||||
child: FileBubble(
|
child: FileBubble(
|
||||||
nameSuggestion,
|
nameSuggestion,
|
||||||
rootHash,
|
rootHash,
|
||||||
|
@ -73,7 +71,6 @@ class FileMessage extends Message {
|
||||||
fileSize,
|
fileSize,
|
||||||
isAuto: metadata.isAuto,
|
isAuto: metadata.isAuto,
|
||||||
interactive: false,
|
interactive: false,
|
||||||
isPreview: true,
|
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:cwtch/models/message.dart';
|
import 'package:cwtch/models/message.dart';
|
||||||
|
import 'package:cwtch/models/messages/malformedmessage.dart';
|
||||||
import 'package:cwtch/widgets/malformedbubble.dart';
|
import 'package:cwtch/widgets/malformedbubble.dart';
|
||||||
import 'package:cwtch/widgets/messagerow.dart';
|
import 'package:cwtch/widgets/messagerow.dart';
|
||||||
import 'package:cwtch/widgets/quotedmessage.dart';
|
import 'package:cwtch/widgets/quotedmessage.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import '../../main.dart';
|
||||||
|
import '../messagecache.dart';
|
||||||
|
import '../profile.dart';
|
||||||
|
|
||||||
class QuotedMessageStructure {
|
class QuotedMessageStructure {
|
||||||
final String quotedHash;
|
final String quotedHash;
|
||||||
final String body;
|
final String body;
|
||||||
|
@ -29,14 +34,8 @@ class QuotedMessage extends Message {
|
||||||
value: this.metadata,
|
value: this.metadata,
|
||||||
builder: (bcontext, child) {
|
builder: (bcontext, child) {
|
||||||
try {
|
try {
|
||||||
dynamic message = jsonDecode(
|
dynamic message = jsonDecode(this.content);
|
||||||
this.content,
|
return Text(message["body"]);
|
||||||
);
|
|
||||||
var content = message["body"];
|
|
||||||
return Text(
|
|
||||||
content,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return MalformedBubble();
|
return MalformedBubble();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:cwtch/models/message.dart';
|
import 'package:cwtch/models/message.dart';
|
||||||
import 'package:cwtch/models/messages/malformedmessage.dart';
|
import 'package:cwtch/models/messages/malformedmessage.dart';
|
||||||
import 'package:cwtch/widgets/malformedbubble.dart';
|
import 'package:cwtch/widgets/malformedbubble.dart';
|
||||||
|
@ -21,10 +19,7 @@ class TextMessage extends Message {
|
||||||
return ChangeNotifierProvider.value(
|
return ChangeNotifierProvider.value(
|
||||||
value: this.metadata,
|
value: this.metadata,
|
||||||
builder: (bcontext, child) {
|
builder: (bcontext, child) {
|
||||||
return Text(
|
return SelectableText(this.content);
|
||||||
this.content,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:cwtch/models/remoteserver.dart';
|
import 'package:cwtch/models/remoteserver.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
|
||||||
|
|
||||||
import 'contact.dart';
|
import 'contact.dart';
|
||||||
import 'contactlist.dart';
|
import 'contactlist.dart';
|
||||||
|
@ -21,7 +20,7 @@ class ProfileInfoState extends ChangeNotifier {
|
||||||
bool _online = false;
|
bool _online = false;
|
||||||
Map<String, FileDownloadProgress> _downloads = Map<String, FileDownloadProgress>();
|
Map<String, FileDownloadProgress> _downloads = Map<String, FileDownloadProgress>();
|
||||||
Map<String, int> _downloadTriggers = Map<String, int>();
|
Map<String, int> _downloadTriggers = Map<String, int>();
|
||||||
ItemScrollController contactListScrollController = new ItemScrollController();
|
|
||||||
// assume profiles are encrypted...this will be set to false
|
// assume profiles are encrypted...this will be set to false
|
||||||
// in the constructor if the profile is encrypted with the defacto password.
|
// in the constructor if the profile is encrypted with the defacto password.
|
||||||
bool _encrypted = true;
|
bool _encrypted = true;
|
||||||
|
|
|
@ -10,6 +10,8 @@ import 'package:desktop_notifications/desktop_notifications.dart' as linux_notif
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:flutter_local_notifications_linux/flutter_local_notifications_linux.dart';
|
import 'package:flutter_local_notifications_linux/flutter_local_notifications_linux.dart';
|
||||||
import 'package:flutter_local_notifications_linux/src/model/hint.dart';
|
import 'package:flutter_local_notifications_linux/src/model/hint.dart';
|
||||||
|
import 'package:flutter_local_notifications_linux/src/model/icon.dart';
|
||||||
|
|
||||||
|
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
@ -55,22 +57,33 @@ class WindowsNotificationManager implements NotificationsManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LinuxNotificationsManager uses the desktop_notifications package to implement
|
class NotificationPayload {
|
||||||
// the standard dbus-powered linux desktop notifications.
|
late String profileOnion;
|
||||||
class LinuxNotificationsManager implements NotificationsManager {
|
late int convoId;
|
||||||
int previous_id = 0;
|
|
||||||
late linux_notifications.NotificationsClient client;
|
|
||||||
late Future<void> Function(String, int) notificationSelectConvo;
|
|
||||||
late String assetsPath;
|
|
||||||
|
|
||||||
LinuxNotificationsManager(Future<void> Function(String, int) notificationSelectConvo) {
|
NotificationPayload(String po, int cid) {
|
||||||
this.client = linux_notifications.NotificationsClient();
|
profileOnion = po;
|
||||||
this.notificationSelectConvo = notificationSelectConvo;
|
convoId = cid;
|
||||||
scheduleMicrotask(() async {
|
|
||||||
assetsPath = await detectLinuxAssetsPath();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NotificationPayload.fromJson(Map<String, dynamic> json)
|
||||||
|
: profileOnion = json['profileOnion'],
|
||||||
|
convoId = json['convoId'];
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'profileOnion': profileOnion,
|
||||||
|
'convoId': convoId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlutterLocalNotificationsPlugin based NotificationManager that handles MacOS and Linux
|
||||||
|
// TODO: Windows support is being worked on, check back and migrate to that too when it lands
|
||||||
|
class NixNotificationManager implements NotificationsManager {
|
||||||
|
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
|
||||||
|
late Future<void> Function(String, int) notificationSelectConvo;
|
||||||
|
late String linuxAssetsPath;
|
||||||
|
|
||||||
|
|
||||||
// Cwtch can install in non flutter supported ways on linux, this code detects where the assets are on Linux
|
// Cwtch can install in non flutter supported ways on linux, this code detects where the assets are on Linux
|
||||||
Future<String> detectLinuxAssetsPath() async {
|
Future<String> detectLinuxAssetsPath() async {
|
||||||
var devStat = FileStat.stat("assets");
|
var devStat = FileStat.stat("assets");
|
||||||
|
@ -90,53 +103,26 @@ class LinuxNotificationsManager implements NotificationsManager {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> notify(String message, String profile, int conversationId) async {
|
|
||||||
var iconPath = Uri.file(path.join(assetsPath, "assets/knott.png"));
|
|
||||||
client.notify(message, appName: "cwtch", appIcon: iconPath.toString(), replacesId: this.previous_id).then((linux_notifications.Notification value) async {
|
|
||||||
previous_id = value.id;
|
|
||||||
if ((await value.closeReason) == linux_notifications.NotificationClosedReason.dismissed) {
|
|
||||||
this.notificationSelectConvo(profile, conversationId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NotificationPayload {
|
|
||||||
late String profileOnion;
|
|
||||||
late int convoId;
|
|
||||||
|
|
||||||
NotificationPayload(String po, int cid) {
|
|
||||||
profileOnion = po;
|
|
||||||
convoId = cid;
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationPayload.fromJson(Map<String, dynamic> json)
|
|
||||||
: profileOnion = json['profileOnion'],
|
|
||||||
convoId = json['convoId'];
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
'profileOnion': profileOnion,
|
|
||||||
'convoId': convoId,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlutterLocalNotificationsPlugin based NotificationManager that handles MacOS
|
|
||||||
// Todo: work with author to allow settings of asset_path so we can use this for Linux and deprecate the LinuxNotificationManager
|
|
||||||
// Todo: it can also handle Android, do we want to migrate away from our manual solution?
|
|
||||||
class NixNotificationManager implements NotificationsManager {
|
|
||||||
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
|
|
||||||
late Future<void> Function(String, int) notificationSelectConvo;
|
|
||||||
|
|
||||||
NixNotificationManager(Future<void> Function(String, int) notificationSelectConvo) {
|
NixNotificationManager(Future<void> Function(String, int) notificationSelectConvo) {
|
||||||
this.notificationSelectConvo = notificationSelectConvo;
|
this.notificationSelectConvo = notificationSelectConvo;
|
||||||
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||||
|
|
||||||
final MacOSInitializationSettings initializationSettingsMacOS = MacOSInitializationSettings(defaultPresentSound: false);
|
|
||||||
final LinuxInitializationSettings initializationSettingsLinux =
|
|
||||||
LinuxInitializationSettings(defaultActionName: 'Open notification', defaultIcon: AssetsLinuxIcon('assets/knott.png'), defaultSuppressSound: true);
|
|
||||||
|
|
||||||
final InitializationSettings initializationSettings = InitializationSettings(android: null, iOS: null, macOS: initializationSettingsMacOS, linux: initializationSettingsLinux);
|
|
||||||
scheduleMicrotask(() async {
|
scheduleMicrotask(() async {
|
||||||
|
|
||||||
|
if (Platform.isLinux) {
|
||||||
|
linuxAssetsPath = await detectLinuxAssetsPath();
|
||||||
|
} else {
|
||||||
|
linuxAssetsPath = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
final MacOSInitializationSettings initializationSettingsMacOS = MacOSInitializationSettings(defaultPresentSound: false);
|
||||||
|
var linuxIcon = FilePathLinuxIcon( path.join(linuxAssetsPath, 'assets/knott.png'));
|
||||||
|
|
||||||
|
final LinuxInitializationSettings initializationSettingsLinux =
|
||||||
|
LinuxInitializationSettings(defaultActionName: 'Open notification', defaultIcon: linuxIcon, defaultSuppressSound: true);
|
||||||
|
|
||||||
|
final InitializationSettings initializationSettings = InitializationSettings(android: null, iOS: null, macOS: initializationSettingsMacOS, linux: initializationSettingsLinux);
|
||||||
|
|
||||||
flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<MacOSFlutterLocalNotificationsPlugin>()?.requestPermissions(
|
flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<MacOSFlutterLocalNotificationsPlugin>()?.requestPermissions(
|
||||||
alert: true,
|
alert: true,
|
||||||
badge: false,
|
badge: false,
|
||||||
|
@ -150,7 +136,8 @@ class NixNotificationManager implements NotificationsManager {
|
||||||
Future<void> notify(String message, String profile, int conversationId) async {
|
Future<void> notify(String message, String profile, int conversationId) async {
|
||||||
if (!globalAppState.focus) {
|
if (!globalAppState.focus) {
|
||||||
// Warning: Only use title field on Linux, body field will render links as clickable
|
// Warning: Only use title field on Linux, body field will render links as clickable
|
||||||
await flutterLocalNotificationsPlugin.show(0, message, '', NotificationDetails(linux: LinuxNotificationDetails(suppressSound: true, category: LinuxNotificationCategory.imReceived())),
|
await flutterLocalNotificationsPlugin.show(0, message, '',
|
||||||
|
NotificationDetails(linux: LinuxNotificationDetails(suppressSound: true, category: LinuxNotificationCategory.imReceived(), icon: FilePathLinuxIcon(path.join(linuxAssetsPath, 'assets/knott.png')))),
|
||||||
payload: jsonEncode(NotificationPayload(profile, conversationId)));
|
payload: jsonEncode(NotificationPayload(profile, conversationId)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,7 +155,7 @@ class NixNotificationManager implements NotificationsManager {
|
||||||
NotificationsManager newDesktopNotificationsManager(Future<void> Function(String profileOnion, int convoId) notificationSelectConvo) {
|
NotificationsManager newDesktopNotificationsManager(Future<void> Function(String profileOnion, int convoId) notificationSelectConvo) {
|
||||||
if (Platform.isLinux && !Platform.isAndroid) {
|
if (Platform.isLinux && !Platform.isAndroid) {
|
||||||
try {
|
try {
|
||||||
return LinuxNotificationsManager(notificationSelectConvo);
|
return NixNotificationManager(notificationSelectConvo);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
EnvironmentConfig.debugLog("Failed to create LinuxNotificationManager. Switching off notifications.");
|
EnvironmentConfig.debugLog("Failed to create LinuxNotificationManager. Switching off notifications.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: EdgeInsets.all(30),
|
margin: EdgeInsets.all(30),
|
||||||
padding: EdgeInsets.all(20),
|
padding: EdgeInsets.all(20),
|
||||||
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [
|
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: [
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: Provider.of<ProfileInfoState>(context).onion.isNotEmpty,
|
visible: Provider.of<ProfileInfoState>(context).onion.isNotEmpty,
|
||||||
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
|
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
|
||||||
|
@ -127,9 +127,6 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
||||||
badgeEdit: Provider.of<Settings>(context).isExperimentEnabled(ImagePreviewsExperiment))))
|
badgeEdit: Provider.of<Settings>(context).isExperimentEnabled(ImagePreviewsExperiment))))
|
||||||
])),
|
])),
|
||||||
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
CwtchLabel(label: AppLocalizations.of(context)!.displayNameLabel),
|
CwtchLabel(label: AppLocalizations.of(context)!.displayNameLabel),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
|
@ -276,71 +273,64 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
Row(
|
||||||
onPressed: _createPressed,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
style: ElevatedButton.styleFrom(
|
children: [
|
||||||
minimumSize: Size(400, 50),
|
Expanded(
|
||||||
maximumSize: Size(800, 50),
|
child: ElevatedButton(
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))),
|
onPressed: _createPressed,
|
||||||
),
|
child: Text(
|
||||||
child: Text(
|
Provider.of<ProfileInfoState>(context).onion.isEmpty ? AppLocalizations.of(context)!.addNewProfileBtn : AppLocalizations.of(context)!.saveProfileBtn,
|
||||||
Provider.of<ProfileInfoState>(context).onion.isEmpty ? AppLocalizations.of(context)!.addNewProfileBtn : AppLocalizations.of(context)!.saveProfileBtn,
|
textAlign: TextAlign.center,
|
||||||
textAlign: TextAlign.center,
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
],
|
||||||
height: 20,
|
|
||||||
),
|
),
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: Provider.of<ProfileInfoState>(context, listen: false).onion.isNotEmpty,
|
visible: Provider.of<ProfileInfoState>(context, listen: false).onion.isNotEmpty,
|
||||||
child: Tooltip(
|
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [
|
||||||
message: AppLocalizations.of(context)!.exportProfileTooltip,
|
SizedBox(
|
||||||
child: ElevatedButton.icon(
|
height: 20,
|
||||||
style: ElevatedButton.styleFrom(
|
),
|
||||||
minimumSize: Size(400, 50),
|
Tooltip(
|
||||||
maximumSize: Size(800, 50),
|
message: AppLocalizations.of(context)!.exportProfileTooltip,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))),
|
child: ElevatedButton.icon(
|
||||||
),
|
onPressed: () {
|
||||||
onPressed: () {
|
if (Platform.isAndroid) {
|
||||||
if (Platform.isAndroid) {
|
Provider.of<FlwtchState>(context, listen: false).cwtch.ExportProfile(ctrlrOnion.value.text, ctrlrOnion.value.text + ".tar.gz");
|
||||||
Provider.of<FlwtchState>(context, listen: false).cwtch.ExportProfile(ctrlrOnion.value.text, ctrlrOnion.value.text + ".tar.gz");
|
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.fileSavedTo + " " + ctrlrOnion.value.text + ".tar.gz"));
|
||||||
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.fileSavedTo + " " + ctrlrOnion.value.text + ".tar.gz"));
|
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
} else {
|
||||||
} else {
|
showCreateFilePicker(context).then((name) {
|
||||||
showCreateFilePicker(context).then((name) {
|
if (name != null) {
|
||||||
if (name != null) {
|
Provider.of<FlwtchState>(context, listen: false).cwtch.ExportProfile(ctrlrOnion.value.text, name);
|
||||||
Provider.of<FlwtchState>(context, listen: false).cwtch.ExportProfile(ctrlrOnion.value.text, name);
|
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.fileSavedTo + " " + name));
|
||||||
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.fileSavedTo + " " + name));
|
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
},
|
||||||
},
|
icon: Icon(Icons.import_export),
|
||||||
icon: Icon(Icons.import_export),
|
label: Text(AppLocalizations.of(context)!.exportProfile),
|
||||||
label: Text(AppLocalizations.of(context)!.exportProfile),
|
))
|
||||||
))),
|
])),
|
||||||
SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: Provider.of<ProfileInfoState>(context, listen: false).onion.isNotEmpty,
|
visible: Provider.of<ProfileInfoState>(context, listen: false).onion.isNotEmpty,
|
||||||
child: Tooltip(
|
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [
|
||||||
message: AppLocalizations.of(context)!.enterCurrentPasswordForDelete,
|
SizedBox(
|
||||||
child: ElevatedButton.icon(
|
height: 20,
|
||||||
style: ElevatedButton.styleFrom(
|
),
|
||||||
minimumSize: Size(400, 50),
|
Tooltip(
|
||||||
maximumSize: Size(800, 50),
|
message: AppLocalizations.of(context)!.enterCurrentPasswordForDelete,
|
||||||
shape: RoundedRectangleBorder(
|
child: ElevatedButton.icon(
|
||||||
side: BorderSide(color: Provider.of<Settings>(context).theme.defaultButtonActiveColor, width: 2.0),
|
onPressed: () {
|
||||||
borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))),
|
showAlertDialog(context);
|
||||||
primary: Provider.of<Settings>(context).theme.backgroundMainColor,
|
},
|
||||||
),
|
icon: Icon(Icons.delete_forever),
|
||||||
onPressed: () {
|
label: Text(AppLocalizations.of(context)!.deleteBtn),
|
||||||
showAlertDialog(context);
|
))
|
||||||
},
|
]))
|
||||||
icon: Icon(Icons.delete_forever),
|
|
||||||
label: Text(AppLocalizations.of(context)!.deleteBtn),
|
|
||||||
)))
|
|
||||||
]))))));
|
]))))));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -348,8 +338,7 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
||||||
|
|
||||||
void _copyOnion() {
|
void _copyOnion() {
|
||||||
Clipboard.setData(new ClipboardData(text: Provider.of<ProfileInfoState>(context, listen: false).onion));
|
Clipboard.setData(new ClipboardData(text: Provider.of<ProfileInfoState>(context, listen: false).onion));
|
||||||
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification));
|
// TODO Toast
|
||||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _createPressed() async {
|
void _createPressed() async {
|
||||||
|
|
|
@ -12,7 +12,6 @@ import 'package:cwtch/widgets/profileimage.dart';
|
||||||
import 'package:cwtch/widgets/textfield.dart';
|
import 'package:cwtch/widgets/textfield.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
|
||||||
import '../main.dart';
|
import '../main.dart';
|
||||||
import '../settings.dart';
|
import '../settings.dart';
|
||||||
import 'addcontactview.dart';
|
import 'addcontactview.dart';
|
||||||
|
@ -30,22 +29,20 @@ class ContactsView extends StatefulWidget {
|
||||||
// selectConversation can be called from anywhere to set the active conversation
|
// selectConversation can be called from anywhere to set the active conversation
|
||||||
void selectConversation(BuildContext context, int handle) {
|
void selectConversation(BuildContext context, int handle) {
|
||||||
// requery instead of using contactinfostate directly because sometimes listview gets confused about data that resorts
|
// requery instead of using contactinfostate directly because sometimes listview gets confused about data that resorts
|
||||||
var unread = Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.unreadMessages;
|
var initialIndex = Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.unreadMessages;
|
||||||
var previouslySelected = Provider.of<AppState>(context, listen: false).selectedConversation;
|
var previouslySelected = Provider.of<AppState>(context, listen: false).selectedConversation;
|
||||||
if (previouslySelected != null) {
|
if (previouslySelected != null) {
|
||||||
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(previouslySelected)!.unselected();
|
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(previouslySelected)!.unselected();
|
||||||
}
|
}
|
||||||
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.selected();
|
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.selected();
|
||||||
|
|
||||||
// triggers update in Double/TripleColumnView
|
// triggers update in Double/TripleColumnView
|
||||||
Provider.of<AppState>(context, listen: false).initialScrollIndex = unread;
|
Provider.of<AppState>(context, listen: false).initialScrollIndex = initialIndex;
|
||||||
Provider.of<AppState>(context, listen: false).selectedConversation = handle;
|
Provider.of<AppState>(context, listen: false).selectedConversation = handle;
|
||||||
Provider.of<AppState>(context, listen: false).selectedIndex = null;
|
Provider.of<AppState>(context, listen: false).selectedIndex = null;
|
||||||
Provider.of<AppState>(context, listen: false).hoveredIndex = -1;
|
Provider.of<AppState>(context, listen: false).hoveredIndex = -1;
|
||||||
// if in singlepane mode, push to the stack
|
// if in singlepane mode, push to the stack
|
||||||
var isLandscape = Provider.of<AppState>(context, listen: false).isLandscape(context);
|
var isLandscape = Provider.of<AppState>(context, listen: false).isLandscape(context);
|
||||||
if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(context, handle);
|
if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(context, handle);
|
||||||
|
|
||||||
// Set last message seen time in backend
|
// Set last message seen time in backend
|
||||||
Provider.of<FlwtchState>(context, listen: false)
|
Provider.of<FlwtchState>(context, listen: false)
|
||||||
.cwtch
|
.cwtch
|
||||||
|
@ -88,35 +85,29 @@ class _ContactsViewState extends State<ContactsView> {
|
||||||
endDrawerEnableOpenDragGesture: false,
|
endDrawerEnableOpenDragGesture: false,
|
||||||
drawerEnableOpenDragGesture: false,
|
drawerEnableOpenDragGesture: false,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: Stack(children: [
|
leading: Row(children: [
|
||||||
Align(
|
IconButton(
|
||||||
alignment: Alignment.center,
|
icon: Icon(Icons.arrow_back),
|
||||||
child: IconButton(
|
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
|
||||||
icon: Icon(Icons.arrow_back),
|
onPressed: () {
|
||||||
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
|
Provider.of<ProfileInfoState>(context, listen: false).recountUnread();
|
||||||
onPressed: () {
|
Provider.of<AppState>(context, listen: false).selectedProfile = "";
|
||||||
Provider.of<ProfileInfoState>(context, listen: false).recountUnread();
|
Navigator.of(context).pop();
|
||||||
Provider.of<AppState>(context, listen: false).selectedProfile = "";
|
},
|
||||||
Navigator.of(context).pop();
|
),
|
||||||
},
|
StreamBuilder<bool>(
|
||||||
)),
|
stream: Provider.of<AppState>(context).getUnreadProfileNotifyStream(),
|
||||||
Positioned(
|
builder: (BuildContext context, AsyncSnapshot<bool> unreadCountSnapshot) {
|
||||||
bottom: 5.0,
|
int unreadCount = Provider.of<ProfileListState>(context).generateUnreadCount(Provider.of<AppState>(context).selectedProfile ?? "");
|
||||||
right: 5.0,
|
|
||||||
child: StreamBuilder<bool>(
|
|
||||||
stream: Provider.of<AppState>(context).getUnreadProfileNotifyStream(),
|
|
||||||
builder: (BuildContext context, AsyncSnapshot<bool> unreadCountSnapshot) {
|
|
||||||
int unreadCount = Provider.of<ProfileListState>(context).generateUnreadCount(Provider.of<AppState>(context).selectedProfile ?? "");
|
|
||||||
|
|
||||||
return Visibility(
|
return Visibility(
|
||||||
visible: unreadCount > 0,
|
visible: unreadCount > 0,
|
||||||
child: CircleAvatar(
|
child: CircleAvatar(
|
||||||
radius: 10.0,
|
radius: 10.0,
|
||||||
backgroundColor: Provider.of<Settings>(context).theme.portraitProfileBadgeColor,
|
backgroundColor: Provider.of<Settings>(context).theme.portraitProfileBadgeColor,
|
||||||
child: Text(unreadCount > 99 ? "99+" : unreadCount.toString(), style: TextStyle(color: Provider.of<Settings>(context).theme.portraitProfileBadgeTextColor, fontSize: 8.0)),
|
child: Text(unreadCount > 99 ? "99+" : unreadCount.toString(), style: TextStyle(color: Provider.of<Settings>(context).theme.portraitProfileBadgeTextColor, fontSize: 8.0)),
|
||||||
));
|
));
|
||||||
}),
|
}),
|
||||||
)
|
|
||||||
]),
|
]),
|
||||||
title: RepaintBoundary(
|
title: RepaintBoundary(
|
||||||
child: Row(children: [
|
child: Row(children: [
|
||||||
|
@ -164,8 +155,6 @@ class _ContactsViewState extends State<ContactsView> {
|
||||||
splashRadius: Material.defaultSplashRadius / 2,
|
splashRadius: Material.defaultSplashRadius / 2,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Clipboard.setData(new ClipboardData(text: Provider.of<ProfileInfoState>(context, listen: false).onion));
|
Clipboard.setData(new ClipboardData(text: Provider.of<ProfileInfoState>(context, listen: false).onion));
|
||||||
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification));
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Manage known Servers
|
// Manage known Servers
|
||||||
|
@ -215,28 +204,11 @@ class _ContactsViewState extends State<ContactsView> {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
var initialScroll =
|
final divided = ListTile.divideTiles(
|
||||||
Provider.of<ProfileInfoState>(context, listen: false).contactList.filteredList().indexWhere((element) => element.identifier == Provider.of<AppState>(context).selectedConversation);
|
context: context,
|
||||||
if (initialScroll < 0) {
|
tiles: tiles,
|
||||||
initialScroll = 0;
|
).toList();
|
||||||
}
|
return RepaintBoundary(child: ListView(children: divided));
|
||||||
|
|
||||||
var contactList = ScrollablePositionedList.separated(
|
|
||||||
itemScrollController: Provider.of<ProfileInfoState>(context).contactListScrollController,
|
|
||||||
itemCount: Provider.of<ContactListState>(context).num,
|
|
||||||
initialScrollIndex: initialScroll,
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: BouncingScrollPhysics(),
|
|
||||||
semanticChildCount: Provider.of<ContactListState>(context).num,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return tiles.elementAt(index);
|
|
||||||
},
|
|
||||||
separatorBuilder: (BuildContext context, int index) {
|
|
||||||
return Divider(height: 1);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return RepaintBoundary(child: contactList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _pushAddContact(bool newGroup) {
|
void _pushAddContact(bool newGroup) {
|
||||||
|
@ -282,86 +254,61 @@ class _ContactsViewState extends State<ContactsView> {
|
||||||
height: 200, // bespoke value courtesy of the [TextField] docs
|
height: 200, // bespoke value courtesy of the [TextField] docs
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(2.0),
|
padding: EdgeInsets.all(10.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
|
||||||
|
Spacer(),
|
||||||
|
Expanded(
|
||||||
|
child: Tooltip(
|
||||||
|
message: AppLocalizations.of(context)!.tooltipAddContact,
|
||||||
|
child: ElevatedButton(
|
||||||
|
child: Text(AppLocalizations.of(context)!.addContact, semanticsLabel: AppLocalizations.of(context)!.addContact),
|
||||||
|
onPressed: () {
|
||||||
|
_pushAddContact(false);
|
||||||
|
},
|
||||||
|
))),
|
||||||
|
Spacer()
|
||||||
|
]),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
Expanded(
|
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
|
||||||
|
Spacer(),
|
||||||
|
Expanded(
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
message: AppLocalizations.of(context)!.tooltipAddContact,
|
message: groupsEnabled ? AppLocalizations.of(context)!.addServerTooltip : AppLocalizations.of(context)!.thisFeatureRequiresGroupExpermientsToBeEnabled,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
style: ElevatedButton.styleFrom(
|
child: Text(AppLocalizations.of(context)!.addServerTitle, semanticsLabel: AppLocalizations.of(context)!.addServerTitle),
|
||||||
minimumSize: Size.fromWidth(double.infinity),
|
|
||||||
maximumSize: Size.fromWidth(400),
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!.addContact,
|
|
||||||
semanticsLabel: AppLocalizations.of(context)!.addContact,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
_pushAddContact(false);
|
|
||||||
},
|
|
||||||
))),
|
|
||||||
SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Tooltip(
|
|
||||||
message: groupsEnabled ? AppLocalizations.of(context)!.addServerTooltip : AppLocalizations.of(context)!.thisFeatureRequiresGroupExpermientsToBeEnabled,
|
|
||||||
child: ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
minimumSize: Size.fromWidth(double.infinity),
|
|
||||||
maximumSize: Size.fromWidth(400),
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!.addServerTitle,
|
|
||||||
semanticsLabel: AppLocalizations.of(context)!.addServerTitle,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
onPressed: groupsEnabled
|
|
||||||
? () {
|
|
||||||
_pushAddContact(false);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Tooltip(
|
|
||||||
message: groupsEnabled ? AppLocalizations.of(context)!.createGroupTitle : AppLocalizations.of(context)!.thisFeatureRequiresGroupExpermientsToBeEnabled,
|
|
||||||
child: ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
minimumSize: Size.fromWidth(double.infinity),
|
|
||||||
maximumSize: Size.fromWidth(400),
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!.createGroupTitle,
|
|
||||||
semanticsLabel: AppLocalizations.of(context)!.createGroupTitle,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
onPressed: groupsEnabled
|
onPressed: groupsEnabled
|
||||||
? () {
|
? () {
|
||||||
_pushAddContact(true);
|
_pushAddContact(false);
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
))),
|
)),
|
||||||
|
),
|
||||||
|
Spacer()
|
||||||
|
]),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
|
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
|
||||||
|
Spacer(),
|
||||||
|
Expanded(
|
||||||
|
child: Tooltip(
|
||||||
|
message: groupsEnabled ? AppLocalizations.of(context)!.createGroupTitle : AppLocalizations.of(context)!.thisFeatureRequiresGroupExpermientsToBeEnabled,
|
||||||
|
child: ElevatedButton(
|
||||||
|
child: Text(AppLocalizations.of(context)!.createGroupTitle, semanticsLabel: AppLocalizations.of(context)!.createGroupTitle),
|
||||||
|
onPressed: groupsEnabled
|
||||||
|
? () {
|
||||||
|
_pushAddContact(true);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
))),
|
||||||
|
Spacer()
|
||||||
|
]),
|
||||||
],
|
],
|
||||||
))),
|
))),
|
||||||
)));
|
)));
|
||||||
|
|
|
@ -161,7 +161,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
||||||
child: Text(getThemeName(context, themeId)), //"ddi_$themeId", key: Key("ddi_$themeId")),
|
child: Text(getThemeName(context, themeId)), //"ddi_$themeId", key: Key("ddi_$themeId")),
|
||||||
);
|
);
|
||||||
}).toList())),
|
}).toList())),
|
||||||
leading: Icon(Icons.palette, color: settings.current().mainTextColor),
|
leading: Icon(CwtchIcons.change_theme, color: settings.current().mainTextColor),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context)!.settingUIColumnPortrait, style: TextStyle(color: settings.current().mainTextColor)),
|
title: Text(AppLocalizations.of(context)!.settingUIColumnPortrait, style: TextStyle(color: settings.current().mainTextColor)),
|
||||||
|
@ -188,7 +188,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
style: TextStyle(color: settings.current().mainTextColor),
|
style: TextStyle(color: settings.current().mainTextColor),
|
||||||
),
|
),
|
||||||
leading: Icon(Icons.stay_primary_landscape, color: settings.current().mainTextColor),
|
leading: Icon(Icons.table_chart, color: settings.current().mainTextColor),
|
||||||
trailing: Container(
|
trailing: Container(
|
||||||
width: MediaQuery.of(context).size.width / 4,
|
width: MediaQuery.of(context).size.width / 4,
|
||||||
child: Container(
|
child: Container(
|
||||||
|
@ -406,7 +406,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
||||||
},
|
},
|
||||||
activeTrackColor: settings.theme.defaultButtonActiveColor,
|
activeTrackColor: settings.theme.defaultButtonActiveColor,
|
||||||
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
|
||||||
secondary: Icon(Icons.photo, color: settings.current().mainTextColor),
|
secondary: Icon(Icons.attach_file, color: settings.current().mainTextColor),
|
||||||
),
|
),
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: settings.isExperimentEnabled(ImagePreviewsExperiment) && !Platform.isAndroid,
|
visible: settings.isExperimentEnabled(ImagePreviewsExperiment) && !Platform.isAndroid,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:ui';
|
|
||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
import 'package:cwtch/cwtch/cwtch.dart';
|
import 'package:cwtch/cwtch/cwtch.dart';
|
||||||
import 'package:cwtch/cwtch_icons_icons.dart';
|
import 'package:cwtch/cwtch_icons_icons.dart';
|
||||||
|
@ -11,7 +10,6 @@ import 'package:cwtch/models/message.dart';
|
||||||
import 'package:cwtch/models/messagecache.dart';
|
import 'package:cwtch/models/messagecache.dart';
|
||||||
import 'package:cwtch/models/messages/quotedmessage.dart';
|
import 'package:cwtch/models/messages/quotedmessage.dart';
|
||||||
import 'package:cwtch/models/profile.dart';
|
import 'package:cwtch/models/profile.dart';
|
||||||
import 'package:cwtch/third_party/linkify/flutter_linkify.dart';
|
|
||||||
import 'package:cwtch/widgets/malformedbubble.dart';
|
import 'package:cwtch/widgets/malformedbubble.dart';
|
||||||
import 'package:cwtch/widgets/messageloadingbubble.dart';
|
import 'package:cwtch/widgets/messageloadingbubble.dart';
|
||||||
import 'package:cwtch/widgets/profileimage.dart';
|
import 'package:cwtch/widgets/profileimage.dart';
|
||||||
|
@ -44,9 +42,8 @@ class _MessageViewState extends State<MessageView> {
|
||||||
final focusNode = FocusNode();
|
final focusNode = FocusNode();
|
||||||
int selectedContact = -1;
|
int selectedContact = -1;
|
||||||
ItemPositionsListener scrollListener = ItemPositionsListener.create();
|
ItemPositionsListener scrollListener = ItemPositionsListener.create();
|
||||||
|
ItemScrollController scrollController = ItemScrollController();
|
||||||
File? imagePreview;
|
File? imagePreview;
|
||||||
bool showDown = false;
|
|
||||||
bool showPreview = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -57,12 +54,6 @@ class _MessageViewState extends State<MessageView> {
|
||||||
Provider.of<AppState>(context, listen: false).initialScrollIndex = 0;
|
Provider.of<AppState>(context, listen: false).initialScrollIndex = 0;
|
||||||
Provider.of<AppState>(context, listen: false).unreadMessagesBelow = false;
|
Provider.of<AppState>(context, listen: false).unreadMessagesBelow = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scrollListener.itemPositions.value.length != 0 && !scrollListener.itemPositions.value.any((element) => element.index == 0)) {
|
|
||||||
showDown = true;
|
|
||||||
} else {
|
|
||||||
showDown = false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
@ -90,11 +81,10 @@ class _MessageViewState extends State<MessageView> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// After leaving a conversation the selected conversation is set to null...
|
// After leaving a conversation the selected conversation is set to null...
|
||||||
if (Provider.of<ContactInfoState>(context, listen: false).profileOnion == "") {
|
if (Provider.of<ContactInfoState>(context).profileOnion == "") {
|
||||||
return Card(child: Center(child: Text(AppLocalizations.of(context)!.addContactFirst)));
|
return Card(child: Center(child: Text(AppLocalizations.of(context)!.addContactFirst)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var showMessageFormattingPreview = Provider.of<Settings>(context).isExperimentEnabled(FormattingExperiment);
|
|
||||||
var showFileSharing = Provider.of<Settings>(context).isExperimentEnabled(FileSharingExperiment);
|
var showFileSharing = Provider.of<Settings>(context).isExperimentEnabled(FileSharingExperiment);
|
||||||
var appBarButtons = <Widget>[];
|
var appBarButtons = <Widget>[];
|
||||||
if (Provider.of<ContactInfoState>(context).isOnline()) {
|
if (Provider.of<ContactInfoState>(context).isOnline()) {
|
||||||
|
@ -138,26 +128,18 @@ class _MessageViewState extends State<MessageView> {
|
||||||
onWillPop: _onWillPop,
|
onWillPop: _onWillPop,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: Provider.of<Settings>(context).theme.backgroundMainColor,
|
backgroundColor: Provider.of<Settings>(context).theme.backgroundMainColor,
|
||||||
floatingActionButton: showDown
|
floatingActionButton: appState.unreadMessagesBelow
|
||||||
? FloatingActionButton(
|
? FloatingActionButton(
|
||||||
child: Icon(Icons.arrow_downward, color: Provider.of<Settings>(context).current().defaultButtonTextColor),
|
child: Icon(Icons.arrow_downward, color: Provider.of<Settings>(context).current().defaultButtonTextColor),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Provider.of<AppState>(context, listen: false).initialScrollIndex = 0;
|
Provider.of<AppState>(context, listen: false).initialScrollIndex = 0;
|
||||||
Provider.of<AppState>(context, listen: false).unreadMessagesBelow = false;
|
Provider.of<AppState>(context, listen: false).unreadMessagesBelow = false;
|
||||||
Provider.of<ContactInfoState>(context, listen: false).messageScrollController.scrollTo(index: 0, duration: Duration(milliseconds: 600));
|
scrollController.scrollTo(index: 0, duration: Duration(milliseconds: 600));
|
||||||
})
|
})
|
||||||
: null,
|
: null,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
// setting leading(Width) to null makes it do the default behaviour; container() hides it
|
// setting leading to null makes it do the default behaviour; container() hides it
|
||||||
leadingWidth: Provider.of<Settings>(context).uiColumns(appState.isLandscape(context)).length > 1 ? 0 : null,
|
leading: Provider.of<Settings>(context).uiColumns(appState.isLandscape(context)).length > 1 ? Container() : null,
|
||||||
leading: Provider.of<Settings>(context).uiColumns(appState.isLandscape(context)).length > 1
|
|
||||||
? Container(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
margin: EdgeInsets.zero,
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
title: Row(children: [
|
title: Row(children: [
|
||||||
ProfileImage(
|
ProfileImage(
|
||||||
imagePath: Provider.of<Settings>(context).isExperimentEnabled(ImagePreviewsExperiment)
|
imagePath: Provider.of<Settings>(context).isExperimentEnabled(ImagePreviewsExperiment)
|
||||||
|
@ -179,12 +161,8 @@ class _MessageViewState extends State<MessageView> {
|
||||||
]),
|
]),
|
||||||
actions: appBarButtons,
|
actions: appBarButtons,
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: Padding(padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 108.0), child: MessageList(scrollController, scrollListener)),
|
||||||
padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 182.0),
|
bottomSheet: _buildComposeBox(),
|
||||||
child: MessageList(
|
|
||||||
scrollListener,
|
|
||||||
)),
|
|
||||||
bottomSheet: showPreview && showMessageFormattingPreview ? _buildPreviewBox() : _buildComposeBox(),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,7 +211,7 @@ class _MessageViewState extends State<MessageView> {
|
||||||
ctrlrCompose.value = TextEditingValue(text: messageWithoutNewLine, selection: TextSelection.fromPosition(TextPosition(offset: messageWithoutNewLine.length)));
|
ctrlrCompose.value = TextEditingValue(text: messageWithoutNewLine, selection: TextSelection.fromPosition(TextPosition(offset: messageWithoutNewLine.length)));
|
||||||
|
|
||||||
// Do this after we trim to preserve enter-behaviour...
|
// Do this after we trim to preserve enter-behaviour...
|
||||||
bool isOffline = Provider.of<ContactInfoState>(context, listen: false).isOnline() == false;
|
bool isOffline = Provider.of<ContactInfoState>(context).isOnline() == false;
|
||||||
if (isOffline) {
|
if (isOffline) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -317,224 +295,64 @@ class _MessageViewState extends State<MessageView> {
|
||||||
Provider.of<FlwtchState>(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, LastMessageSeenTimeKey, DateTime.now().toIso8601String());
|
Provider.of<FlwtchState>(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, LastMessageSeenTimeKey, DateTime.now().toIso8601String());
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildPreviewBox() {
|
Widget _buildComposeBox() {
|
||||||
var showClickableLinks = Provider.of<Settings>(context).isExperimentEnabled(ClickableLinksExperiment);
|
bool isOffline = Provider.of<ContactInfoState>(context).isOnline() == false;
|
||||||
|
bool isGroup = Provider.of<ContactInfoState>(context).isGroup;
|
||||||
|
|
||||||
var wdgMessage = Padding(
|
var charLength = ctrlrCompose.value.text.characters.length;
|
||||||
padding: EdgeInsets.all(8),
|
var expectedLength = ctrlrCompose.value.text.length;
|
||||||
child: SelectableLinkify(
|
var numberOfBytesMoreThanChar = (expectedLength - charLength);
|
||||||
text: ctrlrCompose.text + '\n',
|
|
||||||
options: LinkifyOptions(messageFormatting: true, parseLinks: showClickableLinks, looseUrl: true, defaultToHttps: true),
|
|
||||||
linkifiers: [UrlLinkifier()],
|
|
||||||
onOpen: showClickableLinks ? null : null,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Provider.of<Settings>(context).theme.messageFromMeTextColor,
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
linkStyle: TextStyle(
|
|
||||||
color: Provider.of<Settings>(context).theme.messageFromMeTextColor,
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
codeStyle: TextStyle(
|
|
||||||
// note: these colors are flipped
|
|
||||||
fontSize: 16,
|
|
||||||
color: Provider.of<Settings>(context).theme.messageFromOtherTextColor,
|
|
||||||
backgroundColor: Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor),
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
textWidthBasis: TextWidthBasis.longestLine,
|
|
||||||
));
|
|
||||||
|
|
||||||
var showMessageFormattingPreview = Provider.of<Settings>(context).isExperimentEnabled(FormattingExperiment);
|
|
||||||
var preview = showMessageFormattingPreview
|
|
||||||
? IconButton(
|
|
||||||
tooltip: AppLocalizations.of(context)!.tooltipBackToMessageEditing,
|
|
||||||
icon: Icon(Icons.text_fields),
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
showPreview = false;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
: Container();
|
|
||||||
|
|
||||||
var composeBox = Container(
|
var composeBox = Container(
|
||||||
color: Provider.of<Settings>(context).theme.backgroundMainColor,
|
color: Provider.of<Settings>(context).theme.backgroundMainColor,
|
||||||
padding: EdgeInsets.all(2),
|
padding: EdgeInsets.all(2),
|
||||||
margin: EdgeInsets.all(2),
|
margin: EdgeInsets.all(2),
|
||||||
|
height: 100,
|
||||||
// 164 minimum height + 16px for every line of text so the entire message is displayed when previewed.
|
child: Row(
|
||||||
height: 164 + ((ctrlrCompose.text.split("\n").length - 1) * 16),
|
children: <Widget>[
|
||||||
child: Column(
|
Expanded(
|
||||||
children: [
|
child: Container(
|
||||||
Row(mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [preview]),
|
decoration: BoxDecoration(border: Border(top: BorderSide(color: Provider.of<Settings>(context).theme.defaultButtonActiveColor))),
|
||||||
Container(
|
child: RawKeyboardListener(
|
||||||
decoration: BoxDecoration(border: Border(top: BorderSide(color: Provider.of<Settings>(context).theme.defaultButtonActiveColor))),
|
focusNode: FocusNode(),
|
||||||
child: Row(mainAxisAlignment: MainAxisAlignment.start, children: [wdgMessage])),
|
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,
|
||||||
|
maxLength: (isGroup ? GroupMessageLengthMax : P2PMessageLengthMax) - numberOfBytesMoreThanChar,
|
||||||
|
maxLengthEnforcement: MaxLengthEnforcement.enforced,
|
||||||
|
maxLines: null,
|
||||||
|
onFieldSubmitted: _sendMessage,
|
||||||
|
enabled: true, // always allow editing...
|
||||||
|
onChanged: (String x) {
|
||||||
|
setState(() {
|
||||||
|
// we need to force a rerender here to update the max length count
|
||||||
|
});
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: isOffline ? "" : AppLocalizations.of(context)!.placeholderEnterMessage,
|
||||||
|
hintStyle: TextStyle(color: Provider.of<Settings>(context).theme.sendHintTextColor),
|
||||||
|
enabledBorder: InputBorder.none,
|
||||||
|
focusedBorder: InputBorder.none,
|
||||||
|
enabled: true,
|
||||||
|
suffixIcon: ElevatedButton(
|
||||||
|
key: Key("btnSend"),
|
||||||
|
style: ElevatedButton.styleFrom(padding: EdgeInsets.all(0.0), shape: new RoundedRectangleBorder(borderRadius: new BorderRadius.circular(45.0))),
|
||||||
|
child: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of<Settings>(context).theme.defaultButtonTextColor),
|
||||||
|
onPressed: isOffline ? null : _sendMessage,
|
||||||
|
))),
|
||||||
|
)))),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return Container(
|
|
||||||
color: Provider.of<Settings>(context).theme.backgroundMainColor, child: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [composeBox]));
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildComposeBox() {
|
|
||||||
bool isOffline = Provider.of<ContactInfoState>(context).isOnline() == false;
|
|
||||||
bool isGroup = Provider.of<ContactInfoState>(context).isGroup;
|
|
||||||
var showToolbar = Provider.of<Settings>(context).isExperimentEnabled(FormattingExperiment);
|
|
||||||
var charLength = ctrlrCompose.value.text.characters.length;
|
|
||||||
var expectedLength = ctrlrCompose.value.text.length;
|
|
||||||
var numberOfBytesMoreThanChar = (expectedLength - charLength);
|
|
||||||
|
|
||||||
var bold = IconButton(
|
|
||||||
icon: Icon(Icons.format_bold),
|
|
||||||
tooltip: AppLocalizations.of(context)!.tooltipBoldText,
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text);
|
|
||||||
var selection = ctrlrCompose.selection;
|
|
||||||
var start = ctrlrCompose.selection.start;
|
|
||||||
var end = ctrlrCompose.selection.end;
|
|
||||||
ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "**" + selected + "**");
|
|
||||||
ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 2, extentOffset: selection.start + 2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var italic = IconButton(
|
|
||||||
icon: Icon(Icons.format_italic),
|
|
||||||
tooltip: AppLocalizations.of(context)!.tooltipItalicize,
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text);
|
|
||||||
var selection = ctrlrCompose.selection;
|
|
||||||
var start = ctrlrCompose.selection.start;
|
|
||||||
var end = ctrlrCompose.selection.end;
|
|
||||||
ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "*" + selected + "*");
|
|
||||||
ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 1, extentOffset: selection.start + 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var code = IconButton(
|
|
||||||
icon: Icon(Icons.code),
|
|
||||||
tooltip: AppLocalizations.of(context)!.tooltipCode,
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text);
|
|
||||||
var selection = ctrlrCompose.selection;
|
|
||||||
var start = ctrlrCompose.selection.start;
|
|
||||||
var end = ctrlrCompose.selection.end;
|
|
||||||
ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "`" + selected + "`");
|
|
||||||
ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 1, extentOffset: selection.start + 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var superscript = IconButton(
|
|
||||||
icon: Icon(Icons.superscript),
|
|
||||||
tooltip: AppLocalizations.of(context)!.tooltipSuperscript,
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text);
|
|
||||||
var selection = ctrlrCompose.selection;
|
|
||||||
var start = ctrlrCompose.selection.start;
|
|
||||||
var end = ctrlrCompose.selection.end;
|
|
||||||
ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "^" + selected + "^");
|
|
||||||
ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 1, extentOffset: selection.start + 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var subscript = IconButton(
|
|
||||||
icon: Icon(Icons.subscript),
|
|
||||||
tooltip: AppLocalizations.of(context)!.tooltipSubscript,
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text);
|
|
||||||
var selection = ctrlrCompose.selection;
|
|
||||||
var start = ctrlrCompose.selection.start;
|
|
||||||
var end = ctrlrCompose.selection.end;
|
|
||||||
ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "_" + selected + "_");
|
|
||||||
ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 1, extentOffset: selection.start + 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var strikethrough = IconButton(
|
|
||||||
icon: Icon(Icons.format_strikethrough),
|
|
||||||
tooltip: AppLocalizations.of(context)!.tooltipStrikethrough,
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text);
|
|
||||||
var selection = ctrlrCompose.selection;
|
|
||||||
var start = ctrlrCompose.selection.start;
|
|
||||||
var end = ctrlrCompose.selection.end;
|
|
||||||
ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "~~" + selected + "~~");
|
|
||||||
ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 2, extentOffset: selection.start + 2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var preview = IconButton(
|
|
||||||
icon: Icon(Icons.text_format),
|
|
||||||
tooltip: AppLocalizations.of(context)!.tooltipPreviewFormatting,
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
showPreview = true;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var vline = Padding(
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 1, horizontal: 2),
|
|
||||||
child: Container(height: 16, width: 1, decoration: BoxDecoration(color: Provider.of<Settings>(context).theme.messageFromMeTextColor)));
|
|
||||||
|
|
||||||
var formattingToolbar = Container(
|
|
||||||
decoration: BoxDecoration(color: Provider.of<Settings>(context).theme.defaultButtonActiveColor),
|
|
||||||
child: Row(mainAxisAlignment: MainAxisAlignment.start, children: [bold, italic, code, superscript, subscript, strikethrough, vline, preview]));
|
|
||||||
|
|
||||||
var textField = 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,
|
|
||||||
maxLength: (isGroup ? GroupMessageLengthMax : P2PMessageLengthMax) - numberOfBytesMoreThanChar,
|
|
||||||
maxLengthEnforcement: MaxLengthEnforcement.enforced,
|
|
||||||
maxLines: 3,
|
|
||||||
onFieldSubmitted: _sendMessage,
|
|
||||||
enabled: true, // always allow editing...
|
|
||||||
|
|
||||||
onChanged: (String x) {
|
|
||||||
setState(() {
|
|
||||||
// we need to force a rerender here to update the max length count
|
|
||||||
});
|
|
||||||
},
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: isOffline ? "" : AppLocalizations.of(context)!.placeholderEnterMessage,
|
|
||||||
hintStyle: TextStyle(color: Provider.of<Settings>(context).theme.sendHintTextColor),
|
|
||||||
enabledBorder: InputBorder.none,
|
|
||||||
focusedBorder: InputBorder.none,
|
|
||||||
enabled: true,
|
|
||||||
suffixIcon: ElevatedButton(
|
|
||||||
key: Key("btnSend"),
|
|
||||||
style: ElevatedButton.styleFrom(padding: EdgeInsets.all(0.0), shape: new RoundedRectangleBorder(borderRadius: new BorderRadius.circular(45.0))),
|
|
||||||
child: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of<Settings>(context).theme.defaultButtonTextColor),
|
|
||||||
onPressed: isOffline ? null : _sendMessage,
|
|
||||||
))),
|
|
||||||
)));
|
|
||||||
|
|
||||||
var textEditChildren;
|
|
||||||
if (showToolbar) {
|
|
||||||
textEditChildren = [formattingToolbar, textField];
|
|
||||||
} else {
|
|
||||||
textEditChildren = [textField];
|
|
||||||
}
|
|
||||||
|
|
||||||
var composeBox =
|
|
||||||
Container(color: Provider.of<Settings>(context).theme.backgroundMainColor, padding: EdgeInsets.all(2), margin: EdgeInsets.all(2), height: 164, child: Column(children: textEditChildren));
|
|
||||||
|
|
||||||
var children;
|
var children;
|
||||||
if (Provider.of<AppState>(context).selectedConversation != null && Provider.of<AppState>(context).selectedIndex != null) {
|
if (Provider.of<AppState>(context).selectedConversation != null && Provider.of<AppState>(context).selectedIndex != null) {
|
||||||
|
@ -583,7 +401,7 @@ class _MessageViewState extends State<MessageView> {
|
||||||
children = [composeBox];
|
children = [composeBox];
|
||||||
}
|
}
|
||||||
|
|
||||||
return Container(color: Provider.of<Settings>(context).theme.backgroundMainColor, child: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: children));
|
return Container(color: Provider.of<Settings>(context).theme.backgroundMainColor, child: Column(mainAxisSize: MainAxisSize.min, children: children));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the message if enter is pressed without the shift key...
|
// Send the message if enter is pressed without the shift key...
|
||||||
|
|
|
@ -140,7 +140,7 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
|
||||||
CwtchButtonTextField(
|
CwtchButtonTextField(
|
||||||
controller: TextEditingController(text: Provider.of<ContactInfoState>(context, listen: false).onion),
|
controller: TextEditingController(text: Provider.of<ContactInfoState>(context, listen: false).onion),
|
||||||
onPressed: _copyOnion,
|
onPressed: _copyOnion,
|
||||||
icon: Icon(CwtchIcons.address_copy_2),
|
icon: Icon(Icons.copy),
|
||||||
tooltip: AppLocalizations.of(context)!.copyBtn,
|
tooltip: AppLocalizations.of(context)!.copyBtn,
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
|
|
|
@ -194,66 +194,49 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
|
||||||
padding: EdgeInsets.all(10.0),
|
padding: EdgeInsets.all(10.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
|
||||||
|
Spacer(),
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton(
|
||||||
|
child: Text(AppLocalizations.of(context)!.addProfileTitle, semanticsLabel: AppLocalizations.of(context)!.addProfileTitle),
|
||||||
|
onPressed: () {
|
||||||
|
_pushAddProfile(context);
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
Spacer()
|
||||||
|
]),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
Expanded(
|
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
|
||||||
child: ElevatedButton(
|
Spacer(),
|
||||||
style: ElevatedButton.styleFrom(
|
Expanded(
|
||||||
minimumSize: Size(double.infinity, 20),
|
child: Tooltip(
|
||||||
maximumSize: Size(400, 20),
|
message: AppLocalizations.of(context)!.importProfileTooltip,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))),
|
child: ElevatedButton(
|
||||||
),
|
child: Text(AppLocalizations.of(context)!.importProfile, semanticsLabel: AppLocalizations.of(context)!.importProfile),
|
||||||
child: Text(
|
onPressed: () {
|
||||||
AppLocalizations.of(context)!.addProfileTitle,
|
// 10GB profiles should be enough for anyone?
|
||||||
semanticsLabel: AppLocalizations.of(context)!.addProfileTitle,
|
showFilePicker(context, MaxGeneralFileSharingSize, (file) {
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
showPasswordDialog(context, AppLocalizations.of(context)!.importProfile, AppLocalizations.of(context)!.importProfile, (password) {
|
||||||
),
|
Navigator.popUntil(context, (route) => route.isFirst);
|
||||||
onPressed: () {
|
Provider.of<FlwtchState>(context, listen: false).cwtch.ImportProfile(file.path, password).then((value) {
|
||||||
_pushAddProfile(context);
|
if (value == "") {
|
||||||
},
|
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.successfullyImportedProfile.replaceFirst("%profile", file.path)));
|
||||||
)),
|
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||||
SizedBox(
|
} else {
|
||||||
height: 20,
|
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.failedToImportProfile));
|
||||||
),
|
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||||
Expanded(
|
}
|
||||||
child: Tooltip(
|
});
|
||||||
message: AppLocalizations.of(context)!.importProfileTooltip,
|
|
||||||
child: ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
minimumSize: Size(double.infinity, 20),
|
|
||||||
maximumSize: Size(400, 20),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
side: BorderSide(color: Provider.of<Settings>(context).theme.defaultButtonActiveColor, width: 2.0),
|
|
||||||
borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))),
|
|
||||||
primary: Provider.of<Settings>(context).theme.backgroundMainColor,
|
|
||||||
),
|
|
||||||
child:
|
|
||||||
Text(AppLocalizations.of(context)!.importProfile, semanticsLabel: AppLocalizations.of(context)!.importProfile, style: TextStyle(fontWeight: FontWeight.bold)),
|
|
||||||
onPressed: () {
|
|
||||||
// 10GB profiles should be enough for anyone?
|
|
||||||
showFilePicker(context, MaxGeneralFileSharingSize, (file) {
|
|
||||||
showPasswordDialog(context, AppLocalizations.of(context)!.importProfile, AppLocalizations.of(context)!.importProfile, (password) {
|
|
||||||
Navigator.popUntil(context, (route) => route.isFirst);
|
|
||||||
Provider.of<FlwtchState>(context, listen: false).cwtch.ImportProfile(file.path, password).then((value) {
|
|
||||||
if (value == "") {
|
|
||||||
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.successfullyImportedProfile.replaceFirst("%profile", file.path)));
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
|
||||||
} else {
|
|
||||||
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.failedToImportProfile));
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
}, () {}, () {});
|
||||||
}, () {}, () {});
|
},
|
||||||
},
|
))),
|
||||||
))),
|
Spacer()
|
||||||
SizedBox(
|
]),
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
))),
|
))),
|
||||||
)));
|
)));
|
||||||
|
|
|
@ -3,8 +3,11 @@ import 'dart:io';
|
||||||
import 'package:cwtch/models/appstate.dart';
|
import 'package:cwtch/models/appstate.dart';
|
||||||
import 'package:cwtch/models/contact.dart';
|
import 'package:cwtch/models/contact.dart';
|
||||||
import 'package:cwtch/models/profile.dart';
|
import 'package:cwtch/models/profile.dart';
|
||||||
|
import 'package:cwtch/models/profileservers.dart';
|
||||||
import 'package:cwtch/views/contactsview.dart';
|
import 'package:cwtch/views/contactsview.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/painting.dart';
|
||||||
|
import 'package:cwtch/views/messageview.dart';
|
||||||
import 'package:cwtch/widgets/profileimage.dart';
|
import 'package:cwtch/widgets/profileimage.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
@ -77,52 +80,44 @@ class _ContactRowState extends State<ContactRow> {
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: !Provider.of<Settings>(context).streamerMode,
|
visible: !Provider.of<Settings>(context).streamerMode,
|
||||||
child: Text(contact.onion,
|
child: Text(contact.onion,
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(color: contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedTextColor : Provider.of<Settings>(context).theme.mainTextColor)),
|
style: TextStyle(color: contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedTextColor : Provider.of<Settings>(context).theme.mainTextColor)),
|
||||||
),
|
)
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.all(0),
|
|
||||||
child: contact.isInvitation == true
|
|
||||||
? Wrap(alignment: WrapAlignment.start, direction: Axis.vertical, children: <Widget>[
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.all(2),
|
|
||||||
child: TextButton.icon(
|
|
||||||
label: Text(
|
|
||||||
AppLocalizations.of(context)!.tooltipAcceptContactRequest,
|
|
||||||
),
|
|
||||||
icon: Icon(
|
|
||||||
Icons.favorite,
|
|
||||||
size: 16,
|
|
||||||
color: Provider.of<Settings>(context).theme.mainTextColor,
|
|
||||||
),
|
|
||||||
onPressed: _btnApprove,
|
|
||||||
)),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.all(2),
|
|
||||||
child: TextButton.icon(
|
|
||||||
label: Text(
|
|
||||||
AppLocalizations.of(context)!.tooltipRejectContactRequest,
|
|
||||||
style: TextStyle(decoration: TextDecoration.underline),
|
|
||||||
),
|
|
||||||
style: ButtonStyle(
|
|
||||||
backgroundColor: MaterialStateProperty.all(Provider.of<Settings>(context).theme.backgroundPaneColor),
|
|
||||||
foregroundColor: MaterialStateProperty.all(Provider.of<Settings>(context).theme.mainTextColor)),
|
|
||||||
icon: Icon(Icons.delete, size: 16, color: Provider.of<Settings>(context).theme.mainTextColor),
|
|
||||||
onPressed: _btnReject,
|
|
||||||
))
|
|
||||||
])
|
|
||||||
: (contact.isBlocked != null && contact.isBlocked
|
|
||||||
? IconButton(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
splashRadius: Material.defaultSplashRadius / 2,
|
|
||||||
iconSize: 16,
|
|
||||||
icon: Icon(Icons.block, color: Provider.of<Settings>(context).theme.mainTextColor),
|
|
||||||
onPressed: () {},
|
|
||||||
)
|
|
||||||
: Text(dateToNiceString(contact.lastMessageTime))),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
))),
|
))),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(5.0),
|
||||||
|
child: contact.isInvitation == true
|
||||||
|
? Wrap(direction: Axis.vertical, children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
splashRadius: Material.defaultSplashRadius / 2,
|
||||||
|
iconSize: 16,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.favorite,
|
||||||
|
color: Provider.of<Settings>(context).theme.mainTextColor,
|
||||||
|
),
|
||||||
|
tooltip: AppLocalizations.of(context)!.tooltipAcceptContactRequest,
|
||||||
|
onPressed: _btnApprove,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
splashRadius: Material.defaultSplashRadius / 2,
|
||||||
|
iconSize: 16,
|
||||||
|
icon: Icon(Icons.delete, color: Provider.of<Settings>(context).theme.mainTextColor),
|
||||||
|
tooltip: AppLocalizations.of(context)!.tooltipRejectContactRequest,
|
||||||
|
onPressed: _btnReject,
|
||||||
|
)
|
||||||
|
])
|
||||||
|
: (contact.isBlocked != null && contact.isBlocked
|
||||||
|
? IconButton(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
splashRadius: Material.defaultSplashRadius / 2,
|
||||||
|
iconSize: 16,
|
||||||
|
icon: Icon(Icons.block, color: Provider.of<Settings>(context).theme.mainTextColor),
|
||||||
|
onPressed: () {},
|
||||||
|
)
|
||||||
|
: Text(dateToNiceString(contact.lastMessageTime))),
|
||||||
|
),
|
||||||
]),
|
]),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
selectConversation(context, contact.identifier);
|
selectConversation(context, contact.identifier);
|
||||||
|
@ -153,7 +148,7 @@ class _ContactRowState extends State<ContactRow> {
|
||||||
return AppLocalizations.of(context)!.conversationNotificationPolicyNever;
|
return AppLocalizations.of(context)!.conversationNotificationPolicyNever;
|
||||||
}
|
}
|
||||||
// If the last message was over a day ago, just state the date
|
// If the last message was over a day ago, just state the date
|
||||||
if (DateTime.now().difference(date).inDays > 0) {
|
if (DateTime.now().difference(date).inDays > 1) {
|
||||||
return DateFormat.yMd(Platform.localeName).format(date.toLocal());
|
return DateFormat.yMd(Platform.localeName).format(date.toLocal());
|
||||||
}
|
}
|
||||||
// Otherwise just state the time.
|
// Otherwise just state the time.
|
||||||
|
|
|
@ -25,9 +25,8 @@ class FileBubble extends StatefulWidget {
|
||||||
final int fileSize;
|
final int fileSize;
|
||||||
final bool interactive;
|
final bool interactive;
|
||||||
final bool isAuto;
|
final bool isAuto;
|
||||||
final bool isPreview;
|
|
||||||
|
|
||||||
FileBubble(this.nameSuggestion, this.rootHash, this.nonce, this.fileSize, {this.isAuto = false, this.interactive = true, this.isPreview = false});
|
FileBubble(this.nameSuggestion, this.rootHash, this.nonce, this.fileSize, {this.isAuto = false, this.interactive = true});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FileBubbleState createState() => FileBubbleState();
|
FileBubbleState createState() => FileBubbleState();
|
||||||
|
@ -45,22 +44,6 @@ class FileBubbleState extends State<FileBubble> {
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getPreview(context) {
|
|
||||||
return Image.file(
|
|
||||||
myFile!,
|
|
||||||
cacheWidth: (MediaQuery.of(context).size.width * 0.6).floor(),
|
|
||||||
// limit the amount of space the image can decode too, we keep this high-ish to allow quality previews...
|
|
||||||
filterQuality: FilterQuality.medium,
|
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
height: MediaQuery.of(context).size.height * 0.30,
|
|
||||||
isAntiAlias: false,
|
|
||||||
errorBuilder: (context, error, stackTrace) {
|
|
||||||
return MalformedBubble();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var fromMe = Provider.of<MessageMetadata>(context).senderHandle == Provider.of<ProfileInfoState>(context).onion;
|
var fromMe = Provider.of<MessageMetadata>(context).senderHandle == Provider.of<ProfileInfoState>(context).onion;
|
||||||
|
@ -126,12 +109,6 @@ class FileBubbleState extends State<FileBubble> {
|
||||||
senderDisplayStr = Provider.of<MessageMetadata>(context).senderHandle;
|
senderDisplayStr = Provider.of<MessageMetadata>(context).senderHandle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we don't preview a non downloaded file...
|
|
||||||
if (widget.isPreview && myFile != null) {
|
|
||||||
return getPreview(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
return LayoutBuilder(builder: (bcontext, constraints) {
|
return LayoutBuilder(builder: (bcontext, constraints) {
|
||||||
var wdgSender = Visibility(
|
var wdgSender = Visibility(
|
||||||
visible: widget.interactive,
|
visible: widget.interactive,
|
||||||
|
@ -156,7 +133,21 @@ class FileBubbleState extends State<FileBubble> {
|
||||||
child: MouseRegion(
|
child: MouseRegion(
|
||||||
cursor: SystemMouseCursors.click,
|
cursor: SystemMouseCursors.click,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child: Padding(padding: EdgeInsets.all(1.0), child: getPreview(context)),
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(1.0),
|
||||||
|
child: Image.file(
|
||||||
|
myFile!,
|
||||||
|
cacheWidth: (MediaQuery.of(bcontext).size.width * 0.6).floor(),
|
||||||
|
// limit the amount of space the image can decode too, we keep this high-ish to allow quality previews...
|
||||||
|
filterQuality: FilterQuality.medium,
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
height: MediaQuery.of(bcontext).size.height * 0.30,
|
||||||
|
isAntiAlias: false,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return MalformedBubble();
|
||||||
|
},
|
||||||
|
)),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
pop(bcontext, myFile!, wdgMessage);
|
pop(bcontext, myFile!, wdgMessage);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:cwtch/controllers/open_link_modal.dart';
|
|
||||||
import 'package:cwtch/models/contact.dart';
|
import 'package:cwtch/models/contact.dart';
|
||||||
import 'package:cwtch/models/message.dart';
|
import 'package:cwtch/models/message.dart';
|
||||||
import 'package:cwtch/third_party/linkify/flutter_linkify.dart';
|
import 'package:cwtch/third_party/linkify/flutter_linkify.dart';
|
||||||
import 'package:cwtch/models/profile.dart';
|
import 'package:cwtch/models/profile.dart';
|
||||||
import 'package:cwtch/widgets/malformedbubble.dart';
|
import 'package:cwtch/widgets/malformedbubble.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import '../settings.dart';
|
import '../settings.dart';
|
||||||
import 'messagebubbledecorations.dart';
|
import 'messagebubbledecorations.dart';
|
||||||
|
@ -52,7 +55,7 @@ class MessageBubbleState extends State<MessageBubble> {
|
||||||
linkifiers: [UrlLinkifier()],
|
linkifiers: [UrlLinkifier()],
|
||||||
onOpen: showClickableLinks
|
onOpen: showClickableLinks
|
||||||
? (link) {
|
? (link) {
|
||||||
modalOpenLink(context, link);
|
_modalOpenLink(context, link);
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
//key: Key(myKey),
|
//key: Key(myKey),
|
||||||
|
@ -101,4 +104,59 @@ class MessageBubbleState extends State<MessageBubble> {
|
||||||
children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations])))));
|
children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations])))));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _modalOpenLink(BuildContext ctx, LinkableElement link) {
|
||||||
|
showModalBottomSheet<void>(
|
||||||
|
context: ctx,
|
||||||
|
builder: (BuildContext bcontext) {
|
||||||
|
return Container(
|
||||||
|
height: 200, // bespoke value courtesy of the [TextField] docs
|
||||||
|
child: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(30.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(AppLocalizations.of(bcontext)!.clickableLinksWarning),
|
||||||
|
Flex(direction: Axis.horizontal, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
|
||||||
|
child: ElevatedButton(
|
||||||
|
child: Text(AppLocalizations.of(bcontext)!.clickableLinksCopy, semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinksCopy),
|
||||||
|
onPressed: () {
|
||||||
|
Clipboard.setData(new ClipboardData(text: link.url));
|
||||||
|
|
||||||
|
final snackBar = SnackBar(
|
||||||
|
content: Text(AppLocalizations.of(bcontext)!.copiedToClipboardNotification),
|
||||||
|
);
|
||||||
|
|
||||||
|
Navigator.pop(bcontext);
|
||||||
|
ScaffoldMessenger.of(bcontext).showSnackBar(snackBar);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
|
||||||
|
child: ElevatedButton(
|
||||||
|
child: Text(AppLocalizations.of(bcontext)!.clickableLinkOpen, semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinkOpen),
|
||||||
|
onPressed: () async {
|
||||||
|
if (await canLaunch(link.url)) {
|
||||||
|
await launch(link.url);
|
||||||
|
Navigator.pop(bcontext);
|
||||||
|
} else {
|
||||||
|
final snackBar = SnackBar(
|
||||||
|
content: Text(AppLocalizations.of(bcontext)!.clickableLinkError),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(bcontext).showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,9 @@ import '../main.dart';
|
||||||
import '../settings.dart';
|
import '../settings.dart';
|
||||||
|
|
||||||
class MessageList extends StatefulWidget {
|
class MessageList extends StatefulWidget {
|
||||||
|
ItemScrollController scrollController;
|
||||||
ItemPositionsListener scrollListener;
|
ItemPositionsListener scrollListener;
|
||||||
MessageList(this.scrollListener);
|
MessageList(this.scrollController, this.scrollListener);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_MessageListState createState() => _MessageListState();
|
_MessageListState createState() => _MessageListState();
|
||||||
|
@ -29,6 +30,7 @@ class _MessageListState extends State<MessageList> {
|
||||||
MessageCache? cache = Provider.of<ProfileInfoState>(outerContext, listen: false).contactList.getContact(conversationId)?.messageCache;
|
MessageCache? cache = Provider.of<ProfileInfoState>(outerContext, listen: false).contactList.getContact(conversationId)?.messageCache;
|
||||||
ByIndex(0).loadUnsynced(Provider.of<FlwtchState>(context, listen: false).cwtch, Provider.of<AppState>(outerContext, listen: false).selectedProfile!, conversationId, cache!);
|
ByIndex(0).loadUnsynced(Provider.of<FlwtchState>(context, listen: false).cwtch, Provider.of<AppState>(outerContext, listen: false).selectedProfile!, conversationId, cache!);
|
||||||
}
|
}
|
||||||
|
|
||||||
var initi = Provider.of<AppState>(outerContext, listen: false).initialScrollIndex;
|
var initi = Provider.of<AppState>(outerContext, listen: false).initialScrollIndex;
|
||||||
bool isP2P = !Provider.of<ContactInfoState>(context).isGroup;
|
bool isP2P = !Provider.of<ContactInfoState>(context).isGroup;
|
||||||
bool isGroupAndSyncing = Provider.of<ContactInfoState>(context).isGroup == true && Provider.of<ContactInfoState>(context).status == "Authenticated";
|
bool isGroupAndSyncing = Provider.of<ContactInfoState>(context).isGroup == true && Provider.of<ContactInfoState>(context).status == "Authenticated";
|
||||||
|
@ -80,7 +82,7 @@ class _MessageListState extends State<MessageList> {
|
||||||
child: loadMessages
|
child: loadMessages
|
||||||
? ScrollablePositionedList.builder(
|
? ScrollablePositionedList.builder(
|
||||||
itemPositionsListener: widget.scrollListener,
|
itemPositionsListener: widget.scrollListener,
|
||||||
itemScrollController: Provider.of<ContactInfoState>(outerContext).messageScrollController,
|
itemScrollController: widget.scrollController,
|
||||||
initialScrollIndex: initi > 4 ? initi - 4 : 0,
|
initialScrollIndex: initi > 4 ? initi - 4 : 0,
|
||||||
itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages,
|
itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages,
|
||||||
reverse: true, // NOTE: There seems to be a bug in flutter that corrects the mouse wheel scroll, but not the drag direction...
|
reverse: true, // NOTE: There seems to be a bug in flutter that corrects the mouse wheel scroll, but not the drag direction...
|
||||||
|
|
|
@ -12,7 +12,6 @@ import 'package:cwtch/widgets/profileimage.dart';
|
||||||
import 'package:flutter/physics.dart';
|
import 'package:flutter/physics.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
|
||||||
|
|
||||||
import '../main.dart';
|
import '../main.dart';
|
||||||
import '../settings.dart';
|
import '../settings.dart';
|
||||||
|
@ -275,8 +274,6 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
|
||||||
// Can't happen
|
// Can't happen
|
||||||
} else {
|
} else {
|
||||||
selectConversation(context, id);
|
selectConversation(context, id);
|
||||||
var contactIndex = Provider.of<ProfileInfoState>(context, listen: false).contactList.filteredList().indexWhere((element) => element.identifier == id);
|
|
||||||
Provider.of<ProfileInfoState>(context, listen: false).contactListScrollController.jumpTo(index: contactIndex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
import 'package:cwtch/controllers/open_link_modal.dart';
|
|
||||||
import 'package:cwtch/models/appstate.dart';
|
|
||||||
import 'package:cwtch/models/contact.dart';
|
import 'package:cwtch/models/contact.dart';
|
||||||
import 'package:cwtch/models/message.dart';
|
import 'package:cwtch/models/message.dart';
|
||||||
import 'package:cwtch/models/profile.dart';
|
import 'package:cwtch/models/profile.dart';
|
||||||
import 'package:cwtch/third_party/linkify/flutter_linkify.dart';
|
|
||||||
import 'package:cwtch/views/contactsview.dart';
|
|
||||||
import 'package:cwtch/widgets/malformedbubble.dart';
|
import 'package:cwtch/widgets/malformedbubble.dart';
|
||||||
import 'package:cwtch/widgets/messageloadingbubble.dart';
|
import 'package:cwtch/widgets/messageloadingbubble.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
@ -48,29 +43,12 @@ class QuotedMessageBubbleState extends State<QuotedMessageBubble> {
|
||||||
var wdgSender = SelectableText(senderDisplayStr,
|
var wdgSender = SelectableText(senderDisplayStr,
|
||||||
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor));
|
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor));
|
||||||
|
|
||||||
var showClickableLinks = Provider.of<Settings>(context).isExperimentEnabled(ClickableLinksExperiment);
|
var wdgMessage = SelectableText(
|
||||||
var formatMessages = Provider.of<Settings>(context).isExperimentEnabled(FormattingExperiment);
|
widget.body + '\u202F',
|
||||||
|
|
||||||
var wdgMessage = SelectableLinkify(
|
|
||||||
text: widget.body + '\u202F',
|
|
||||||
// TODO: onOpen breaks the "selectable" functionality. Maybe something to do with gesture handler?
|
|
||||||
options: LinkifyOptions(messageFormatting: formatMessages, parseLinks: showClickableLinks, looseUrl: true, defaultToHttps: true),
|
|
||||||
linkifiers: [UrlLinkifier()],
|
|
||||||
onOpen: showClickableLinks
|
|
||||||
? (link) {
|
|
||||||
modalOpenLink(context, link);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
//key: Key(myKey),
|
|
||||||
focusNode: _focus,
|
focusNode: _focus,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor,
|
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor,
|
||||||
),
|
),
|
||||||
linkStyle: TextStyle(color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor),
|
|
||||||
codeStyle: TextStyle(
|
|
||||||
// note: these colors are flipped
|
|
||||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromOtherTextColor : Provider.of<Settings>(context).theme.messageFromMeTextColor,
|
|
||||||
backgroundColor: fromMe ? Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor : Provider.of<Settings>(context).theme.messageFromMeBackgroundColor),
|
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
textWidthBasis: TextWidthBasis.longestLine,
|
textWidthBasis: TextWidthBasis.longestLine,
|
||||||
);
|
);
|
||||||
|
@ -83,33 +61,14 @@ class QuotedMessageBubbleState extends State<QuotedMessageBubble> {
|
||||||
var qMessage = (snapshot.data! as Message);
|
var qMessage = (snapshot.data! as Message);
|
||||||
// Swap the background color for quoted tweets..
|
// Swap the background color for quoted tweets..
|
||||||
var qTextColor = fromMe ? Provider.of<Settings>(context).theme.messageFromOtherTextColor : Provider.of<Settings>(context).theme.messageFromMeTextColor;
|
var qTextColor = fromMe ? Provider.of<Settings>(context).theme.messageFromOtherTextColor : Provider.of<Settings>(context).theme.messageFromMeTextColor;
|
||||||
return MouseRegion(
|
return Container(
|
||||||
cursor: SystemMouseCursors.click,
|
margin: EdgeInsets.all(5),
|
||||||
child: GestureDetector(
|
padding: EdgeInsets.all(5),
|
||||||
onTap: () {
|
color: fromMe ? Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor : Provider.of<Settings>(context).theme.messageFromMeBackgroundColor,
|
||||||
var index = Provider.of<ContactInfoState>(context, listen: false).messageCache.cacheByHash[qMessage.getMetadata().contenthash];
|
child: Wrap(runAlignment: WrapAlignment.spaceEvenly, alignment: WrapAlignment.spaceEvenly, runSpacing: 1.0, crossAxisAlignment: WrapCrossAlignment.center, children: [
|
||||||
var totalMessages = Provider.of<ContactInfoState>(context, listen: false).totalMessages;
|
Center(widthFactor: 1, child: Padding(padding: EdgeInsets.all(10.0), child: Icon(Icons.reply, size: 32, color: qTextColor))),
|
||||||
// we have to reverse here because the list itself is reversed...
|
Center(widthFactor: 1.0, child: DefaultTextStyle(child: qMessage.getPreviewWidget(context), style: TextStyle(color: qTextColor)))
|
||||||
Provider.of<ContactInfoState>(context).messageScrollController.scrollTo(index: totalMessages - index!, duration: Duration(milliseconds: 100));
|
]));
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
margin: EdgeInsets.all(5),
|
|
||||||
padding: EdgeInsets.all(5),
|
|
||||||
clipBehavior: Clip.antiAlias,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor : Provider.of<Settings>(context).theme.messageFromMeBackgroundColor,
|
|
||||||
),
|
|
||||||
height: 75,
|
|
||||||
child: Row(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, children: [
|
|
||||||
Padding(padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0), child: Icon(Icons.reply, size: 32, color: qTextColor)),
|
|
||||||
Flexible(
|
|
||||||
child: DefaultTextStyle(
|
|
||||||
textWidthBasis: TextWidthBasis.parent,
|
|
||||||
child: qMessage.getPreviewWidget(context),
|
|
||||||
style: TextStyle(color: qTextColor),
|
|
||||||
overflow: TextOverflow.fade,
|
|
||||||
))
|
|
||||||
]))));
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
return MalformedBubble();
|
return MalformedBubble();
|
||||||
|
|
|
@ -61,8 +61,6 @@ class _ServerRowState extends State<ServerRow> {
|
||||||
icon: Icon(CwtchIcons.address_copy_2, color: Provider.of<Settings>(context).current().mainTextColor),
|
icon: Icon(CwtchIcons.address_copy_2, color: Provider.of<Settings>(context).current().mainTextColor),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Clipboard.setData(new ClipboardData(text: server.serverBundle));
|
Clipboard.setData(new ClipboardData(text: server.serverBundle));
|
||||||
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification));
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,14 @@
|
||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <screen_retriever/screen_retriever_plugin.h>
|
||||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
#include <window_manager/window_manager_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
|
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
||||||
|
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
|
|
|
@ -3,10 +3,14 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
screen_retriever
|
||||||
url_launcher_linux
|
url_launcher_linux
|
||||||
window_manager
|
window_manager
|
||||||
)
|
)
|
||||||
|
|
||||||
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
|
||||||
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||||
|
@ -15,3 +19,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||||
endforeach(plugin)
|
endforeach(plugin)
|
||||||
|
|
||||||
|
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
|
||||||
|
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
|
||||||
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
|
||||||
|
endforeach(ffi_plugin)
|
||||||
|
|
|
@ -5,12 +5,18 @@
|
||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import flutter_local_notifications
|
||||||
import package_info_plus_macos
|
import package_info_plus_macos
|
||||||
import path_provider_macos
|
import path_provider_macos
|
||||||
|
import screen_retriever
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
|
import window_manager
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||||
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
|
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
|
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
||||||
}
|
}
|
||||||
|
|
176
pubspec.lock
176
pubspec.lock
|
@ -15,27 +15,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.8.0"
|
version: "2.8.0"
|
||||||
ansicolor:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: ansicolor
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.1"
|
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.6"
|
version: "3.1.11"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.1"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -56,7 +49,7 @@ packages:
|
||||||
name: build
|
name: build
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.3.0"
|
||||||
build_config:
|
build_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -70,7 +63,7 @@ packages:
|
||||||
name: build_daemon
|
name: build_daemon
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.1.0"
|
||||||
build_resolvers:
|
build_resolvers:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -84,7 +77,7 @@ packages:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.7"
|
version: "2.1.11"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -105,7 +98,7 @@ packages:
|
||||||
name: built_value
|
name: built_value
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.1.4"
|
version: "8.3.3"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -127,6 +120,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
cli_dialog:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cli_dialog
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.0"
|
||||||
cli_util:
|
cli_util:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -154,14 +154,14 @@ packages:
|
||||||
name: collection
|
name: collection
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0"
|
version: "1.16.0"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: convert
|
name: convert
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.2"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -175,7 +175,14 @@ packages:
|
||||||
name: cupertino_icons
|
name: cupertino_icons
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.5"
|
||||||
|
dart_console:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dart_console
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
dart_style:
|
dart_style:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -189,7 +196,7 @@ packages:
|
||||||
name: dbus
|
name: dbus
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.1"
|
version: "0.7.4"
|
||||||
desktop_notifications:
|
desktop_notifications:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -203,14 +210,14 @@ packages:
|
||||||
name: fake_async
|
name: fake_async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.3.0"
|
||||||
ffi:
|
ffi:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: ffi
|
name: ffi
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.2"
|
version: "1.2.1"
|
||||||
file:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -224,7 +231,7 @@ packages:
|
||||||
name: file_picker
|
name: file_picker
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.3.3"
|
version: "4.6.1"
|
||||||
file_picker_desktop:
|
file_picker_desktop:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -238,7 +245,7 @@ packages:
|
||||||
name: fixnum
|
name: fixnum
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.1"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -262,14 +269,14 @@ packages:
|
||||||
name: flutter_local_notifications
|
name: flutter_local_notifications
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.3.2"
|
version: "9.6.1"
|
||||||
flutter_local_notifications_linux:
|
flutter_local_notifications_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_local_notifications_linux
|
name: flutter_local_notifications_linux
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.2"
|
version: "0.5.0+1"
|
||||||
flutter_local_notifications_platform_interface:
|
flutter_local_notifications_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -288,7 +295,7 @@ packages:
|
||||||
name: flutter_plugin_android_lifecycle
|
name: flutter_plugin_android_lifecycle
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.5"
|
version: "2.0.6"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -305,12 +312,19 @@ packages:
|
||||||
name: frontend_server_client
|
name: frontend_server_client
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.3"
|
||||||
fuchsia_remote_debug_protocol:
|
fuchsia_remote_debug_protocol:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
get_it:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: get_it
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "7.2.0"
|
||||||
gherkin:
|
gherkin:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -324,7 +338,7 @@ packages:
|
||||||
name: glob
|
name: glob
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.1.0"
|
||||||
graphs:
|
graphs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -345,21 +359,21 @@ packages:
|
||||||
name: http_multi_server
|
name: http_multi_server
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.2.1"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http_parser
|
name: http_parser
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.0"
|
version: "4.0.1"
|
||||||
image:
|
image:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.2.0"
|
||||||
integration_test:
|
integration_test:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -385,14 +399,14 @@ packages:
|
||||||
name: js
|
name: js
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.3"
|
version: "0.6.4"
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: json_annotation
|
name: json_annotation
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.4.0"
|
version: "4.5.0"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -407,6 +421,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.11"
|
version: "0.12.11"
|
||||||
|
material_color_utilities:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: material_color_utilities
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.4"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -420,14 +441,14 @@ packages:
|
||||||
name: mime
|
name: mime
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.2"
|
||||||
msix:
|
msix:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: msix
|
name: msix
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.8.3"
|
version: "3.6.2"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -441,21 +462,21 @@ packages:
|
||||||
name: package_config
|
name: package_config
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.1.0"
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: package_info_plus
|
name: package_info_plus
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.4.2"
|
||||||
package_info_plus_linux:
|
package_info_plus_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_info_plus_linux
|
name: package_info_plus_linux
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "1.0.5"
|
||||||
package_info_plus_macos:
|
package_info_plus_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -476,84 +497,84 @@ packages:
|
||||||
name: package_info_plus_web
|
name: package_info_plus_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.5"
|
||||||
package_info_plus_windows:
|
package_info_plus_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_info_plus_windows
|
name: package_info_plus_windows
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.5"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0"
|
version: "1.8.1"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.8"
|
version: "2.0.11"
|
||||||
path_provider_android:
|
path_provider_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.11"
|
version: "2.0.15"
|
||||||
path_provider_ios:
|
path_provider_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_ios
|
name: path_provider_ios
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.7"
|
version: "2.0.10"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_linux
|
name: path_provider_linux
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.5"
|
version: "2.1.7"
|
||||||
path_provider_macos:
|
path_provider_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_macos
|
name: path_provider_macos
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.5"
|
version: "2.0.6"
|
||||||
path_provider_platform_interface:
|
path_provider_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_platform_interface
|
name: path_provider_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.3"
|
version: "2.0.4"
|
||||||
path_provider_windows:
|
path_provider_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_windows
|
name: path_provider_windows
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.5"
|
version: "2.0.7"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: petitparser
|
name: petitparser
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.4.0"
|
version: "5.0.0"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: platform
|
name: platform
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.1.0"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -567,7 +588,7 @@ packages:
|
||||||
name: pool
|
name: pool
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.0"
|
version: "1.5.1"
|
||||||
process:
|
process:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -581,14 +602,14 @@ packages:
|
||||||
name: provider
|
name: provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "6.0.3"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pub_semver
|
name: pub_semver
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.1"
|
||||||
pubspec_parse:
|
pubspec_parse:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -596,27 +617,34 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.0"
|
||||||
|
screen_retriever:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: screen_retriever
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.2"
|
||||||
scrollable_positioned_list:
|
scrollable_positioned_list:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: scrollable_positioned_list
|
name: scrollable_positioned_list
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.3"
|
version: "0.3.2"
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf
|
name: shelf
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.3.1"
|
||||||
shelf_web_socket:
|
shelf_web_socket:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf_web_socket
|
name: shelf_web_socket
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.2"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -628,14 +656,14 @@ packages:
|
||||||
name: source_gen
|
name: source_gen
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.2"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.1"
|
version: "1.8.2"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -684,7 +712,7 @@ packages:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.3"
|
version: "0.4.9"
|
||||||
timezone:
|
timezone:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -712,70 +740,70 @@ packages:
|
||||||
name: url_launcher
|
name: url_launcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.18"
|
version: "6.1.3"
|
||||||
url_launcher_android:
|
url_launcher_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_android
|
name: url_launcher_android
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.14"
|
version: "6.0.17"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_ios
|
name: url_launcher_ios
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.14"
|
version: "6.0.17"
|
||||||
url_launcher_linux:
|
url_launcher_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_linux
|
name: url_launcher_linux
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.3"
|
version: "3.0.1"
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_macos
|
name: url_launcher_macos
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.3"
|
version: "3.0.1"
|
||||||
url_launcher_platform_interface:
|
url_launcher_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_platform_interface
|
name: url_launcher_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.5"
|
version: "2.1.0"
|
||||||
url_launcher_web:
|
url_launcher_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_web
|
name: url_launcher_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.6"
|
version: "2.0.12"
|
||||||
url_launcher_windows:
|
url_launcher_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_windows
|
name: url_launcher_windows
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "3.0.1"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_math
|
name: vector_math
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
vm_service:
|
vm_service:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.3.0"
|
version: "8.2.2"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -789,7 +817,7 @@ packages:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.2.0"
|
||||||
webdriver:
|
webdriver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -803,7 +831,7 @@ packages:
|
||||||
name: win32
|
name: win32
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.8"
|
version: "2.6.1"
|
||||||
win_toast:
|
win_toast:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -817,7 +845,7 @@ packages:
|
||||||
name: window_manager
|
name: window_manager
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.4"
|
version: "0.2.5"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -831,14 +859,14 @@ packages:
|
||||||
name: xml
|
name: xml
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.3.1"
|
version: "6.1.0"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: yaml
|
name: yaml
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.15.0 <3.0.0"
|
dart: ">=2.17.0 <3.0.0"
|
||||||
flutter: ">=2.5.0"
|
flutter: ">=3.0.0"
|
||||||
|
|
14
pubspec.yaml
14
pubspec.yaml
|
@ -23,7 +23,7 @@ environment:
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
provider: 5.0.0
|
provider: ^6.0.3
|
||||||
package_info_plus: ^1.0.0
|
package_info_plus: ^1.0.0
|
||||||
#intl_translation: any
|
#intl_translation: any
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
|
@ -32,23 +32,23 @@ dependencies:
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.0
|
cupertino_icons: ^1.0.0
|
||||||
ffi: ^1.0.0
|
ffi: ^1.2.1
|
||||||
path_provider: ^2.0.0
|
path_provider: ^2.0.0
|
||||||
crypto: 3.0.1
|
crypto: 3.0.1
|
||||||
|
|
||||||
glob: any
|
glob: any
|
||||||
scrollable_positioned_list: ^0.2.0-nullsafety.0
|
scrollable_positioned_list: ^0.3.2
|
||||||
file_picker: ^4.3.2
|
file_picker: ^4.3.2
|
||||||
file_picker_desktop: ^1.1.0
|
file_picker_desktop: ^1.1.1
|
||||||
url_launcher: ^6.0.18
|
url_launcher: ^6.0.18
|
||||||
window_manager: ^0.1.4
|
window_manager: ^0.2.5
|
||||||
# notification plugins
|
# notification plugins
|
||||||
win_toast: ^0.0.2
|
win_toast: ^0.0.2
|
||||||
flutter_local_notifications: 9.3.2
|
flutter_local_notifications: ^9.6.1
|
||||||
desktop_notifications: ^0.6.3
|
desktop_notifications: ^0.6.3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
msix: ^2.1.3
|
msix: ^3.6.2
|
||||||
flutter_gherkin: ^3.0.0-rc.9
|
flutter_gherkin: ^3.0.0-rc.9
|
||||||
build_runner: any
|
build_runner: any
|
||||||
# integration_test: any
|
# integration_test: any
|
||||||
|
|
|
@ -6,11 +6,14 @@
|
||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <screen_retriever/screen_retriever_plugin.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
#include <win_toast/win_toast_plugin.h>
|
#include <win_toast/win_toast_plugin.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
#include <window_manager/window_manager_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
ScreenRetrieverPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
WinToastPluginRegisterWithRegistrar(
|
WinToastPluginRegisterWithRegistrar(
|
||||||
|
|
|
@ -3,11 +3,15 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
screen_retriever
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
win_toast
|
win_toast
|
||||||
window_manager
|
window_manager
|
||||||
)
|
)
|
||||||
|
|
||||||
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
|
||||||
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||||
|
@ -16,3 +20,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||||
endforeach(plugin)
|
endforeach(plugin)
|
||||||
|
|
||||||
|
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
|
||||||
|
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
|
||||||
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
|
||||||
|
endforeach(ffi_plugin)
|
||||||
|
|
Loading…
Reference in New Issue