forked from cwtch.im/cwtch-ui
Merge branch 'trunk' of git.openprivacy.ca:cwtch.im/cwtch-ui into notifix
This commit is contained in:
commit
644d6b9793
51
.drone.yml
51
.drone.yml
|
@ -11,7 +11,7 @@ steps:
|
||||||
image: cirrusci/flutter:dev
|
image: cirrusci/flutter:dev
|
||||||
environment:
|
environment:
|
||||||
buildbot_key_b64:
|
buildbot_key_b64:
|
||||||
from_secret: buildbot_key_b64
|
from_secret: buildbot_key_b64
|
||||||
commands:
|
commands:
|
||||||
- mkdir ~/.ssh
|
- mkdir ~/.ssh
|
||||||
- echo $buildbot_key_b64 > ~/.ssh/id_rsa.b64
|
- echo $buildbot_key_b64 > ~/.ssh/id_rsa.b64
|
||||||
|
@ -68,8 +68,8 @@ steps:
|
||||||
|
|
||||||
- name: test-build-android
|
- name: test-build-android
|
||||||
image: cirrusci/flutter:dev
|
image: cirrusci/flutter:dev
|
||||||
when:
|
when:
|
||||||
event: pull_request
|
event: pull_request
|
||||||
volumes:
|
volumes:
|
||||||
- name: deps
|
- name: deps
|
||||||
path: /root/.pub-cache
|
path: /root/.pub-cache
|
||||||
|
@ -106,7 +106,7 @@ steps:
|
||||||
- name: deps
|
- name: deps
|
||||||
path: /root/.pub-cache
|
path: /root/.pub-cache
|
||||||
commands:
|
commands:
|
||||||
# - flutter config --enable-linux-desktop
|
# - flutter config --enable-linux-desktop
|
||||||
- flutter test --coverage
|
- flutter test --coverage
|
||||||
- genhtml coverage/lcov.info -o coverage/html
|
- genhtml coverage/lcov.info -o coverage/html
|
||||||
|
|
||||||
|
@ -209,21 +209,10 @@ steps:
|
||||||
|
|
||||||
- name: build-windows
|
- name: build-windows
|
||||||
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
|
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
|
||||||
environment:
|
|
||||||
pfx:
|
|
||||||
from_secret: pfx
|
|
||||||
pfx_pass:
|
|
||||||
from_secret: pfx_pass
|
|
||||||
commands:
|
commands:
|
||||||
- flutter pub get
|
- flutter pub get
|
||||||
- $Env:version += type .\VERSION
|
- $Env:version += type .\VERSION
|
||||||
- $Env:builddate += type .\BUILDDATE
|
- $Env:builddate += type .\BUILDDATE
|
||||||
- $Env:buildname = 'flwtch-win-' + $Env:version + '-' + $Env:builddate
|
|
||||||
- $Env:builddir = $Env:buildname
|
|
||||||
- $Env:zip = 'cwtch-' + $Env:version + '.zip'
|
|
||||||
- $Env:zipsha = $Env:zip + '.sha512'
|
|
||||||
- $Env:msix = 'cwtch-install-' + $Env:version + '.msix'
|
|
||||||
- $Env:msixsha = $Env:msix + '.sha512'
|
|
||||||
- $Env:releasedir = "build\\windows\\runner\\Release\\"
|
- $Env:releasedir = "build\\windows\\runner\\Release\\"
|
||||||
- flutter build windows --dart-define BUILD_VER=$Env:version --dart-define BUILD_DATE=$Env:builddate
|
- flutter build windows --dart-define BUILD_VER=$Env:version --dart-define BUILD_DATE=$Env:builddate
|
||||||
- copy windows\libCwtch.dll $Env:releasedir
|
- copy windows\libCwtch.dll $Env:releasedir
|
||||||
|
@ -234,22 +223,46 @@ steps:
|
||||||
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\vcruntime140_1.dll $Env:releasedir
|
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\vcruntime140_1.dll $Env:releasedir
|
||||||
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\msvcp140.dll $Env:releasedir
|
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\msvcp140.dll $Env:releasedir
|
||||||
- powershell -command "Expand-Archive -Path tor.zip -DestinationPath $Env:releasedir\Tor"
|
- powershell -command "Expand-Archive -Path tor.zip -DestinationPath $Env:releasedir\Tor"
|
||||||
|
|
||||||
|
- name: package-windows
|
||||||
|
image: openpriv/nsis:latest
|
||||||
|
environment:
|
||||||
|
pfx:
|
||||||
|
from_secret: pfx
|
||||||
|
pfx_pass:
|
||||||
|
from_secret: pfx_pass
|
||||||
|
commands:
|
||||||
|
- $Env:version += type .\VERSION
|
||||||
|
- $Env:builddate += type .\BUILDDATE
|
||||||
|
- $Env:releasedir = "build\\windows\\runner\\Release\\"
|
||||||
|
- $Env:zip = 'cwtch-' + $Env:version + '.zip'
|
||||||
|
- $Env:zipsha = $Env:zip + '.sha512'
|
||||||
|
- $Env:msix = 'cwtch-install-' + $Env:version + '.msix'
|
||||||
|
- $Env:msixsha = $Env:msix + '.sha512'
|
||||||
|
- $Env:buildname = 'flwtch-win-' + $Env:version + '-' + $Env:builddate
|
||||||
|
- $Env:builddir = $Env:buildname
|
||||||
- echo $Env:pfx > codesign.pfx.b64
|
- echo $Env:pfx > codesign.pfx.b64
|
||||||
- certutil -decode codesign.pfx.b64 codesign.pfx
|
- certutil -decode codesign.pfx.b64 codesign.pfx
|
||||||
- C:\MSIX-Toolkit\MSIX-Toolkit.x64\signtool sign /v /fd sha256 /a /f codesign.pfx /p $Env:pfx_pass /tr http://timestamp.digicert.com $Env:releasedir\cwtch.exe
|
- signtool sign /v /fd sha256 /a /f codesign.pfx /p $Env:pfx_pass /tr http://timestamp.digicert.com $Env:releasedir\cwtch.exe
|
||||||
|
- copy windows\runner\resources\knot_128.ico $Env:releasedir\cwtch.ico
|
||||||
|
- makensis windows\nsis\cwtch-installer.nsi
|
||||||
|
- move windows\nsis\cwtch-installer.exe cwtch-installer.exe
|
||||||
|
- signtool sign /v /fd sha256 /a /f codesign.pfx /p $Env:pfx_pass /tr http://timestamp.digicert.com cwtch-installer.exe
|
||||||
|
- powershell -command "(Get-FileHash cwtch-installer.exe -Algorithm sha512).Hash" > cwtch-installer.sha512
|
||||||
- mkdir deploy
|
- mkdir deploy
|
||||||
- mkdir deploy\$Env:builddir
|
- mkdir deploy\$Env:builddir
|
||||||
- move $Env:releasedir $Env:builddir
|
- move $Env:releasedir $Env:builddir
|
||||||
- powershell -command "Compress-Archive -Path $Env:builddir -DestinationPath cwtch.zip"
|
- powershell -command "Compress-Archive -Path $Env:builddir -DestinationPath cwtch.zip"
|
||||||
- powershell -command "(Get-FileHash cwtch.zip -Algorithm sha512).Hash" > $Env:zipsha
|
- powershell -command "(Get-FileHash cwtch.zip -Algorithm sha512).Hash" > $Env:zipsha
|
||||||
|
- move cwtch-installer.exe deploy\$Env:builddir\cwtch-installer.exe
|
||||||
- move cwtch.zip deploy\$Env:builddir\$Env:zip
|
- move cwtch.zip deploy\$Env:builddir\$Env:zip
|
||||||
- move $Env:zipsha deploy\$Env:builddir
|
- move *.sha512 deploy\$Env:builddir
|
||||||
|
|
||||||
- name: deploy-windows
|
- name: deploy-windows
|
||||||
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
|
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
|
||||||
when:
|
when:
|
||||||
event: push
|
event: push
|
||||||
status: [ success ]
|
status: [ success ]
|
||||||
environment:
|
environment:
|
||||||
BUILDFILES_KEY:
|
BUILDFILES_KEY:
|
||||||
from_secret: buildfiles_key
|
from_secret: buildfiles_key
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
v1.0.0-7-g520d35a-2021-06-25-16-34
|
v1.0.0-20-gf8eedca-2021-06-30-20-48
|
|
@ -178,7 +178,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
|
||||||
"CreateGroup" -> {
|
"CreateGroup" -> {
|
||||||
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
||||||
val server = (a.get("server") as? String) ?: ""
|
val server = (a.get("server") as? String) ?: ""
|
||||||
val groupName = (a.get("groupname") as? String) ?: ""
|
val groupName = (a.get("groupName") as? String) ?: ""
|
||||||
Cwtch.createGroup(profile, server, groupName)
|
Cwtch.createGroup(profile, server, groupName)
|
||||||
}
|
}
|
||||||
"DeleteProfile" -> {
|
"DeleteProfile" -> {
|
||||||
|
|
|
@ -245,6 +245,7 @@ class CwtchNotifier {
|
||||||
break;
|
break;
|
||||||
case "ServerStateChange":
|
case "ServerStateChange":
|
||||||
// Update the Server Cache
|
// Update the Server Cache
|
||||||
|
EnvironmentConfig.debugLog("server state changes $data");
|
||||||
profileCN.getProfile(data["ProfileOnion"])?.updateServerStatusCache(data["GroupServer"], data["ConnectionState"]);
|
profileCN.getProfile(data["ProfileOnion"])?.updateServerStatusCache(data["GroupServer"], data["ConnectionState"]);
|
||||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.contacts.forEach((contact) {
|
profileCN.getProfile(data["ProfileOnion"])?.contactList.contacts.forEach((contact) {
|
||||||
if (contact.isGroup == true && contact.server == data["GroupServer"]) {
|
if (contact.isGroup == true && contact.server == data["GroupServer"]) {
|
||||||
|
|
|
@ -115,6 +115,5 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''');
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''');
|
||||||
|
|
||||||
|
|
||||||
yield LicenseEntryWithLineBreaks(["flaticons"], "Icons made by Freepik (https://www.freepik.com) from Flaticon (www.flaticon.com)");
|
yield LicenseEntryWithLineBreaks(["flaticons"], "Icons made by Freepik (https://www.freepik.com) from Flaticon (www.flaticon.com)");
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,7 +190,8 @@ class FlwtchState extends State<Flwtch> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else { //dual pane
|
} else {
|
||||||
|
//dual pane
|
||||||
Provider.of<AppState>(navKey.currentContext!, listen: false).selectedProfile = args["ProfileOnion"];
|
Provider.of<AppState>(navKey.currentContext!, listen: false).selectedProfile = args["ProfileOnion"];
|
||||||
Provider.of<AppState>(navKey.currentContext!, listen: false).selectedConversation = args["Handle"];
|
Provider.of<AppState>(navKey.currentContext!, listen: false).selectedConversation = args["Handle"];
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,7 @@ class LinuxNotificationsManager implements NotificationsManager {
|
||||||
}
|
}
|
||||||
Future<void> notify(String message) async {
|
Future<void> notify(String message) async {
|
||||||
var iconPath = Uri.file(path.join(path.current, "cwtch.png"));
|
var iconPath = Uri.file(path.join(path.current, "cwtch.png"));
|
||||||
client.notify(message, appName: "cwtch",
|
client.notify(message, appName: "cwtch", appIcon: iconPath.toString(), replacesId: this.previous_id).then((Notification value) => previous_id = value.id);
|
||||||
appIcon: iconPath.toString(),
|
|
||||||
replacesId: this.previous_id).then((Notification value) =>
|
|
||||||
previous_id = value.id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,4 +37,4 @@ NotificationsManager newDesktopNotificationsManager() {
|
||||||
print("Attempted to access DBUS for notifications but failed. Switching off notifications.");
|
print("Attempted to access DBUS for notifications but failed. Switching off notifications.");
|
||||||
}
|
}
|
||||||
return NullNotificationsManager();
|
return NullNotificationsManager();
|
||||||
}
|
}
|
||||||
|
|
|
@ -314,7 +314,6 @@ abstract class OpaqueThemeType {
|
||||||
double contactOnionTextSize() {
|
double contactOnionTextSize() {
|
||||||
return 18;
|
return 18;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class OpaqueDark extends OpaqueThemeType {
|
class OpaqueDark extends OpaqueThemeType {
|
||||||
|
@ -1440,4 +1439,4 @@ class Opaque extends OpaqueThemeType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -161,26 +161,40 @@ class Settings extends ChangeNotifier {
|
||||||
|
|
||||||
List<int> uiColumns(bool isLandscape) {
|
List<int> uiColumns(bool isLandscape) {
|
||||||
var m = (!isLandscape || uiColumnModeLandscape == DualpaneMode.CopyPortrait) ? uiColumnModePortrait : uiColumnModeLandscape;
|
var m = (!isLandscape || uiColumnModeLandscape == DualpaneMode.CopyPortrait) ? uiColumnModePortrait : uiColumnModeLandscape;
|
||||||
switch(m) {
|
switch (m) {
|
||||||
case DualpaneMode.Single: return [1];
|
case DualpaneMode.Single:
|
||||||
case DualpaneMode.Dual1to2: return [1, 2];
|
return [1];
|
||||||
case DualpaneMode.Dual1to4: return [1, 4];
|
case DualpaneMode.Dual1to2:
|
||||||
|
return [1, 2];
|
||||||
|
case DualpaneMode.Dual1to4:
|
||||||
|
return [1, 4];
|
||||||
}
|
}
|
||||||
print("impossible column configuration: portrait/$uiColumnModePortrait landscape/$uiColumnModeLandscape");
|
print("impossible column configuration: portrait/$uiColumnModePortrait landscape/$uiColumnModeLandscape");
|
||||||
return [1];
|
return [1];
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<DualpaneMode> uiColumnModeOptions(bool isLandscape) {
|
static List<DualpaneMode> uiColumnModeOptions(bool isLandscape) {
|
||||||
if (isLandscape) return [DualpaneMode.CopyPortrait, DualpaneMode.Single, DualpaneMode.Dual1to2, DualpaneMode.Dual1to4,];
|
if (isLandscape)
|
||||||
else return [DualpaneMode.Single, DualpaneMode.Dual1to2, DualpaneMode.Dual1to4];
|
return [
|
||||||
|
DualpaneMode.CopyPortrait,
|
||||||
|
DualpaneMode.Single,
|
||||||
|
DualpaneMode.Dual1to2,
|
||||||
|
DualpaneMode.Dual1to4,
|
||||||
|
];
|
||||||
|
else
|
||||||
|
return [DualpaneMode.Single, DualpaneMode.Dual1to2, DualpaneMode.Dual1to4];
|
||||||
}
|
}
|
||||||
|
|
||||||
static DualpaneMode uiColumnModeFromString(String m) {
|
static DualpaneMode uiColumnModeFromString(String m) {
|
||||||
switch(m) {
|
switch (m) {
|
||||||
case "DualpaneMode.Single": return DualpaneMode.Single;
|
case "DualpaneMode.Single":
|
||||||
case "DualpaneMode.Dual1to2": return DualpaneMode.Dual1to2;
|
return DualpaneMode.Single;
|
||||||
case "DualpaneMode.Dual1to4": return DualpaneMode.Dual1to4;
|
case "DualpaneMode.Dual1to2":
|
||||||
case "DualpaneMode.CopyPortrait": return DualpaneMode.CopyPortrait;
|
return DualpaneMode.Dual1to2;
|
||||||
|
case "DualpaneMode.Dual1to4":
|
||||||
|
return DualpaneMode.Dual1to4;
|
||||||
|
case "DualpaneMode.CopyPortrait":
|
||||||
|
return DualpaneMode.CopyPortrait;
|
||||||
}
|
}
|
||||||
print("Error: ui requested translation of column mode [$m] which doesn't exist");
|
print("Error: ui requested translation of column mode [$m] which doesn't exist");
|
||||||
return DualpaneMode.Single;
|
return DualpaneMode.Single;
|
||||||
|
@ -188,11 +202,15 @@ class Settings extends ChangeNotifier {
|
||||||
|
|
||||||
static String uiColumnModeToString(DualpaneMode m) {
|
static String uiColumnModeToString(DualpaneMode m) {
|
||||||
// todo: translate
|
// todo: translate
|
||||||
switch(m) {
|
switch (m) {
|
||||||
case DualpaneMode.Single: return "Single";
|
case DualpaneMode.Single:
|
||||||
case DualpaneMode.Dual1to2: return "Double (1:2)";
|
return "Single";
|
||||||
case DualpaneMode.Dual1to4: return "Double (1:4)";
|
case DualpaneMode.Dual1to2:
|
||||||
case DualpaneMode.CopyPortrait: return "Same as portrait mode setting";
|
return "Double (1:2)";
|
||||||
|
case DualpaneMode.Dual1to4:
|
||||||
|
return "Double (1:4)";
|
||||||
|
case DualpaneMode.CopyPortrait:
|
||||||
|
return "Same as portrait mode setting";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -152,9 +152,14 @@ class _AddContactViewState extends State<AddContactView> {
|
||||||
|
|
||||||
Future.delayed(const Duration(milliseconds: 500), () {
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
if (globalErrorHandler.importBundleSuccess) {
|
if (globalErrorHandler.importBundleSuccess) {
|
||||||
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.successfullAddedContact + importBundle));
|
// TODO: This isn't ideal, but because onChange can be fired during this future check
|
||||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
// and because the context can change after being popped we have this kind of double assertion...
|
||||||
Navigator.pop(context);
|
// There is probably a better pattern to handle this...
|
||||||
|
if (AppLocalizations.of(context) != null) {
|
||||||
|
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.successfullAddedContact + importBundle));
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||||
|
Navigator.popUntil(context, (route) => route.settings.name == "conversations");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -166,7 +166,10 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
||||||
autoFillHints: [AutofillHints.newPassword],
|
autoFillHints: [AutofillHints.newPassword],
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
// Password field can be empty when just updating the profile, not on creation
|
// Password field can be empty when just updating the profile, not on creation
|
||||||
if (Provider.of<ProfileInfoState>(context).isEncrypted && Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty && value.isEmpty && usePassword) {
|
if (Provider.of<ProfileInfoState>(context).isEncrypted &&
|
||||||
|
Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty &&
|
||||||
|
value.isEmpty &&
|
||||||
|
usePassword) {
|
||||||
return AppLocalizations.of(context)!.passwordErrorEmpty;
|
return AppLocalizations.of(context)!.passwordErrorEmpty;
|
||||||
}
|
}
|
||||||
if (Provider.of<ErrorHandler>(context).deleteProfileError == true) {
|
if (Provider.of<ErrorHandler>(context).deleteProfileError == true) {
|
||||||
|
|
|
@ -29,7 +29,7 @@ class _DoubleColumnViewState extends State<DoubleColumnView> {
|
||||||
Flexible(
|
Flexible(
|
||||||
flex: cols[1],
|
flex: cols[1],
|
||||||
child: flwtch.selectedConversation == null
|
child: flwtch.selectedConversation == null
|
||||||
? Card(child:Center(child: Text(AppLocalizations.of(context)!.addContactFirst)))
|
? Card(child: Center(child: Text(AppLocalizations.of(context)!.addContactFirst)))
|
||||||
: //dev
|
: //dev
|
||||||
MultiProvider(providers: [
|
MultiProvider(providers: [
|
||||||
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context)),
|
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context)),
|
||||||
|
|
|
@ -169,7 +169,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
||||||
)),
|
)),
|
||||||
AboutListTile(
|
AboutListTile(
|
||||||
icon: Icon(Icons.info, color: settings.current().mainTextColor()),
|
icon: Icon(Icons.info, color: settings.current().mainTextColor()),
|
||||||
applicationIcon: Padding(padding:EdgeInsets.all(5), child: Icon(CwtchIcons.cwtch_knott)),
|
applicationIcon: Padding(padding: EdgeInsets.all(5), child: Icon(CwtchIcons.cwtch_knott)),
|
||||||
applicationName: "Cwtch (Flutter UI)",
|
applicationName: "Cwtch (Flutter UI)",
|
||||||
applicationVersion: AppLocalizations.of(context)!.versionBuilddate.replaceAll("%1", EnvironmentConfig.BUILD_VER).replaceAll("%2", EnvironmentConfig.BUILD_DATE),
|
applicationVersion: AppLocalizations.of(context)!.versionBuilddate.replaceAll("%1", EnvironmentConfig.BUILD_VER).replaceAll("%2", EnvironmentConfig.BUILD_DATE),
|
||||||
applicationLegalese: '\u{a9} 2021 Open Privacy Research Society',
|
applicationLegalese: '\u{a9} 2021 Open Privacy Research Society',
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cwtch/views/peersettingsview.dart';
|
import 'package:cwtch/views/peersettingsview.dart';
|
||||||
import 'package:cwtch/widgets/DropdownContacts.dart';
|
import 'package:cwtch/widgets/DropdownContacts.dart';
|
||||||
|
import 'package:flutter/services.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';
|
||||||
|
|
||||||
|
@ -50,17 +51,19 @@ class _MessageViewState extends State<MessageView> {
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
// setting leading to null makes it do the default behaviour; container() hides it
|
// setting leading to null makes it do the default behaviour; container() hides it
|
||||||
leading: Provider.of<Settings>(context).uiColumns(appState.isLandscape(context)).length > 1 ? Container() : null,
|
leading: Provider.of<Settings>(context).uiColumns(appState.isLandscape(context)).length > 1 ? Container() : null,
|
||||||
title: Row(children: [
|
title: Row(children: [
|
||||||
ProfileImage(
|
ProfileImage(
|
||||||
imagePath: Provider.of<ContactInfoState>(context).imagePath,
|
imagePath: Provider.of<ContactInfoState>(context).imagePath,
|
||||||
diameter: 42,
|
diameter: 42,
|
||||||
border: Provider.of<Settings>(context).current().portraitOnlineBorderColor(),
|
border: Provider.of<Settings>(context).current().portraitOnlineBorderColor(),
|
||||||
badgeTextColor: Colors.red,
|
badgeTextColor: Colors.red,
|
||||||
badgeColor: Colors.red,
|
badgeColor: Colors.red,
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 10,
|
width: 10,
|
||||||
),Text(Provider.of<ContactInfoState>(context).nickname)]),
|
),
|
||||||
|
Text(Provider.of<ContactInfoState>(context).nickname)
|
||||||
|
]),
|
||||||
actions: [
|
actions: [
|
||||||
//IconButton(icon: Icon(Icons.chat), onPressed: _pushContactSettings),
|
//IconButton(icon: Icon(Icons.chat), onPressed: _pushContactSettings),
|
||||||
//IconButton(icon: Icon(Icons.list), onPressed: _pushContactSettings),
|
//IconButton(icon: Icon(Icons.list), onPressed: _pushContactSettings),
|
||||||
|
@ -100,11 +103,13 @@ class _MessageViewState extends State<MessageView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _sendMessage([String? ignoredParam]) {
|
void _sendMessage([String? ignoredParam]) {
|
||||||
ChatMessage cm = new ChatMessage(o: 1, d: ctrlrCompose.value.text);
|
if (ctrlrCompose.value.text.isNotEmpty) {
|
||||||
Provider.of<FlwtchState>(context, listen: false)
|
ChatMessage cm = new ChatMessage(o: 1, d: ctrlrCompose.value.text);
|
||||||
.cwtch
|
Provider.of<FlwtchState>(context, listen: false)
|
||||||
.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).onion, jsonEncode(cm));
|
.cwtch
|
||||||
_sendMessageHelper();
|
.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).onion, jsonEncode(cm));
|
||||||
|
_sendMessageHelper();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _sendInvitation([String? ignoredParam]) {
|
void _sendInvitation([String? ignoredParam]) {
|
||||||
|
@ -135,12 +140,18 @@ class _MessageViewState extends State<MessageView> {
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(border: Border(top: BorderSide(color: Provider.of<Settings>(context).theme.defaultButtonActiveColor()))),
|
decoration: BoxDecoration(border: Border(top: BorderSide(color: Provider.of<Settings>(context).theme.defaultButtonActiveColor()))),
|
||||||
|
child: RawKeyboardListener(
|
||||||
|
focusNode: FocusNode(),
|
||||||
|
onKey: handleKeyPress,
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
key: Key('txtCompose'),
|
key: Key('txtCompose'),
|
||||||
controller: ctrlrCompose,
|
controller: ctrlrCompose,
|
||||||
autofocus: !Platform.isAndroid,
|
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
textInputAction: TextInputAction.send,
|
autofocus: !Platform.isAndroid,
|
||||||
|
textInputAction: TextInputAction.newline,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
minLines: 1,
|
||||||
|
maxLines: null,
|
||||||
onFieldSubmitted: _sendMessage,
|
onFieldSubmitted: _sendMessage,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
enabledBorder: InputBorder.none,
|
enabledBorder: InputBorder.none,
|
||||||
|
@ -158,13 +169,26 @@ class _MessageViewState extends State<MessageView> {
|
||||||
tooltip: AppLocalizations.of(context)!.sendMessage,
|
tooltip: AppLocalizations.of(context)!.sendMessage,
|
||||||
onPressed: _sendMessage,
|
onPressed: _sendMessage,
|
||||||
),
|
),
|
||||||
))),
|
)))),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send the message if enter is pressed without the shift key...
|
||||||
|
void handleKeyPress(event) {
|
||||||
|
var data = event.data as RawKeyEventData;
|
||||||
|
if (data.logicalKey == LogicalKeyboardKey.enter && !event.isShiftPressed) {
|
||||||
|
final messageWithoutNewLine = ctrlrCompose.value.text.trimRight();
|
||||||
|
ctrlrCompose.value = TextEditingValue(
|
||||||
|
text: messageWithoutNewLine
|
||||||
|
);
|
||||||
|
_sendMessage();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void placeHolder() => {};
|
void placeHolder() => {};
|
||||||
|
|
||||||
// explicitly passing BuildContext ctx here is important, change at risk to own health
|
// explicitly passing BuildContext ctx here is important, change at risk to own health
|
||||||
|
|
|
@ -40,45 +40,41 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
|
||||||
// Prevents Android back button from closing the app on the profile manager screen
|
// Prevents Android back button from closing the app on the profile manager screen
|
||||||
// (which would shutdown connections and all kinds of other expensive to generate things)
|
// (which would shutdown connections and all kinds of other expensive to generate things)
|
||||||
// TODO pop up a dialogue regarding closing the app?
|
// TODO pop up a dialogue regarding closing the app?
|
||||||
builder: (context, settings, child) =>
|
builder: (context, settings, child) => WillPopScope(
|
||||||
WillPopScope(
|
onWillPop: () async {
|
||||||
onWillPop: () async {
|
_modalShutdown();
|
||||||
_modalShutdown();
|
return Provider.of<AppState>(context, listen: false).cwtchIsClosing;
|
||||||
return Provider.of<AppState>(context, listen: false).cwtchIsClosing;
|
},
|
||||||
},
|
child: Scaffold(
|
||||||
child: Scaffold(
|
backgroundColor: settings.theme.backgroundMainColor(),
|
||||||
backgroundColor: settings.theme.backgroundMainColor(),
|
appBar: AppBar(
|
||||||
appBar: AppBar(
|
title: Row(children: [
|
||||||
title: Row(children: [
|
Image(
|
||||||
Image(
|
image: AssetImage("assets/core/knott-white.png"),
|
||||||
image: AssetImage("assets/core/knott-white.png"),
|
filterQuality: FilterQuality.medium,
|
||||||
filterQuality: FilterQuality.medium,
|
isAntiAlias: true,
|
||||||
isAntiAlias: true,
|
width: 32,
|
||||||
width: 32,
|
height: 32,
|
||||||
height: 32,
|
colorBlendMode: BlendMode.dstIn,
|
||||||
colorBlendMode: BlendMode.dstIn,
|
color: Provider.of<Settings>(context).theme.backgroundHilightElementColor(),
|
||||||
color: Provider
|
|
||||||
.of<Settings>(context)
|
|
||||||
.theme
|
|
||||||
.backgroundHilightElementColor(),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 10,
|
|
||||||
),
|
|
||||||
Expanded(child: Text(AppLocalizations.of(context)!.titleManageProfiles, style: TextStyle(color: settings.current().mainTextColor())))
|
|
||||||
]),
|
|
||||||
actions: getActions(),
|
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
SizedBox(
|
||||||
onPressed: _pushAddEditProfile,
|
width: 10,
|
||||||
tooltip: AppLocalizations.of(context)!.addNewProfileBtn,
|
|
||||||
child: Icon(
|
|
||||||
Icons.add,
|
|
||||||
semanticLabel: AppLocalizations.of(context)!.addNewProfileBtn,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
body: _buildProfileManager(),
|
Expanded(child: Text(AppLocalizations.of(context)!.titleManageProfiles, style: TextStyle(color: settings.current().mainTextColor())))
|
||||||
)),
|
]),
|
||||||
|
actions: getActions(),
|
||||||
|
),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
onPressed: _pushAddEditProfile,
|
||||||
|
tooltip: AppLocalizations.of(context)!.addNewProfileBtn,
|
||||||
|
child: Icon(
|
||||||
|
Icons.add,
|
||||||
|
semanticLabel: AppLocalizations.of(context)!.addNewProfileBtn,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: _buildProfileManager(),
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,11 @@ class _ContactRowState extends State<ContactRow> {
|
||||||
diameter: 64.0,
|
diameter: 64.0,
|
||||||
imagePath: contact.imagePath,
|
imagePath: contact.imagePath,
|
||||||
maskOut: !contact.isOnline(),
|
maskOut: !contact.isOnline(),
|
||||||
border: contact.isOnline() ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor() : contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedBorderColor() : Provider.of<Settings>(context).theme.portraitOfflineBorderColor()),
|
border: contact.isOnline()
|
||||||
|
? Provider.of<Settings>(context).theme.portraitOnlineBorderColor()
|
||||||
|
: contact.isBlocked
|
||||||
|
? Provider.of<Settings>(context).theme.portraitBlockedBorderColor()
|
||||||
|
: Provider.of<Settings>(context).theme.portraitOfflineBorderColor()),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
@ -44,13 +48,16 @@ class _ContactRowState extends State<ContactRow> {
|
||||||
Text(
|
Text(
|
||||||
contact.nickname, //(contact.isInvitation ? "invite " : "non-invite ") + (contact.isBlocked ? "blokt" : "nonblokt"),//
|
contact.nickname, //(contact.isInvitation ? "invite " : "non-invite ") + (contact.isBlocked ? "blokt" : "nonblokt"),//
|
||||||
|
|
||||||
style: TextStyle(fontSize: Provider.of<Settings>(context).theme.contactOnionTextSize(),
|
style: TextStyle(
|
||||||
color: contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedTextColor() : Provider.of<Settings>(context).theme.mainTextColor()), //Provider.of<FlwtchState>(context).biggerFont,
|
fontSize: Provider.of<Settings>(context).theme.contactOnionTextSize(),
|
||||||
|
color: contact.isBlocked
|
||||||
|
? Provider.of<Settings>(context).theme.portraitBlockedTextColor()
|
||||||
|
: Provider.of<Settings>(context).theme.mainTextColor()), //Provider.of<FlwtchState>(context).biggerFont,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
overflow: TextOverflow.visible,
|
overflow: TextOverflow.visible,
|
||||||
),
|
),
|
||||||
Text(contact.onion,
|
Text(contact.onion,
|
||||||
style: TextStyle(color: contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedTextColor() : Provider.of<Settings>(context).theme.mainTextColor())),
|
style: TextStyle(color: contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedTextColor() : Provider.of<Settings>(context).theme.mainTextColor())),
|
||||||
],
|
],
|
||||||
))),
|
))),
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -60,7 +67,10 @@ class _ContactRowState extends State<ContactRow> {
|
||||||
IconButton(
|
IconButton(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
iconSize: 16,
|
iconSize: 16,
|
||||||
icon: Icon(Icons.favorite, color: Provider.of<Settings>(context).theme.mainTextColor(),),
|
icon: Icon(
|
||||||
|
Icons.favorite,
|
||||||
|
color: Provider.of<Settings>(context).theme.mainTextColor(),
|
||||||
|
),
|
||||||
tooltip: AppLocalizations.of(context)!.tooltipAcceptContactRequest,
|
tooltip: AppLocalizations.of(context)!.tooltipAcceptContactRequest,
|
||||||
onPressed: _btnApprove,
|
onPressed: _btnApprove,
|
||||||
),
|
),
|
||||||
|
|
|
@ -66,18 +66,18 @@ class InvitationBubbleState extends State<InvitationBubble> {
|
||||||
return MalformedBubble();
|
return MalformedBubble();
|
||||||
}
|
}
|
||||||
|
|
||||||
var wdgMessage = isGroup && !showGroupInvite ?
|
var wdgMessage = isGroup && !showGroupInvite
|
||||||
Text(AppLocalizations.of(context)!.groupInviteSettingsWarning) :
|
? Text(AppLocalizations.of(context)!.groupInviteSettingsWarning)
|
||||||
fromMe
|
: fromMe
|
||||||
? senderInviteChrome(AppLocalizations.of(context)!.sendAnInvitation,
|
? senderInviteChrome(AppLocalizations.of(context)!.sendAnInvitation,
|
||||||
isGroup ? Provider.of<ProfileInfoState>(context).contactList.getContact(Provider.of<MessageState>(context).inviteTarget)!.nickname : Provider.of<MessageState>(context).message, myKey)
|
isGroup ? Provider.of<ProfileInfoState>(context).contactList.getContact(Provider.of<MessageState>(context).inviteTarget)!.nickname : Provider.of<MessageState>(context).message, myKey)
|
||||||
: (inviteChrome(isGroup ? AppLocalizations.of(context)!.inviteToGroup : AppLocalizations.of(context)!.contactSuggestion, Provider.of<MessageState>(context).inviteNick,
|
: (inviteChrome(isGroup ? AppLocalizations.of(context)!.inviteToGroup : AppLocalizations.of(context)!.contactSuggestion, Provider.of<MessageState>(context).inviteNick,
|
||||||
Provider.of<MessageState>(context).inviteTarget, myKey));
|
Provider.of<MessageState>(context).inviteTarget, myKey));
|
||||||
|
|
||||||
Widget wdgDecorations;
|
Widget wdgDecorations;
|
||||||
if (isGroup && !showGroupInvite) {
|
if (isGroup && !showGroupInvite) {
|
||||||
wdgDecorations = Text('\u202F');
|
wdgDecorations = Text('\u202F');
|
||||||
} else if (fromMe) {
|
} else if (fromMe) {
|
||||||
wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageState>(context).ackd, errored: Provider.of<MessageState>(context).error, fromMe: fromMe, prettyDate: prettyDate);
|
wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageState>(context).ackd, errored: Provider.of<MessageState>(context).error, fromMe: fromMe, prettyDate: prettyDate);
|
||||||
} else if (isAccepted) {
|
} else if (isAccepted) {
|
||||||
wdgDecorations = Text(AppLocalizations.of(context)!.accepted + '\u202F');
|
wdgDecorations = Text(AppLocalizations.of(context)!.accepted + '\u202F');
|
||||||
|
@ -113,7 +113,8 @@ class InvitationBubbleState extends State<InvitationBubble> {
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(9.0),
|
padding: EdgeInsets.all(9.0),
|
||||||
child: Wrap(runAlignment: WrapAlignment.spaceEvenly, alignment: WrapAlignment.spaceEvenly, runSpacing: 1.0, crossAxisAlignment: WrapCrossAlignment.center, children: [
|
child: Wrap(runAlignment: WrapAlignment.spaceEvenly, alignment: WrapAlignment.spaceEvenly, runSpacing: 1.0, crossAxisAlignment: WrapCrossAlignment.center, children: [
|
||||||
Center(widthFactor: 1, child: Padding(padding: EdgeInsets.all(10.0), child: Icon(isGroup && !showGroupInvite ? CwtchIcons.enable_experiments : CwtchIcons.send_invite, size: 32))),
|
Center(
|
||||||
|
widthFactor: 1, child: Padding(padding: EdgeInsets.all(10.0), child: Icon(isGroup && !showGroupInvite ? CwtchIcons.enable_experiments : CwtchIcons.send_invite, size: 32))),
|
||||||
Center(
|
Center(
|
||||||
widthFactor: 1.0,
|
widthFactor: 1.0,
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
|
@ -16,16 +16,15 @@ class _MessageListState extends State<MessageList> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext outerContext) {
|
Widget build(BuildContext outerContext) {
|
||||||
|
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";
|
||||||
bool isGroupAndSynced = Provider.of<ContactInfoState>(context).isGroup && Provider.of<ContactInfoState>(context).status == "Synced";
|
bool isGroupAndSynced = Provider.of<ContactInfoState>(context).isGroup && Provider.of<ContactInfoState>(context).status == "Synced";
|
||||||
bool isGroupAndNotAuthenticated= Provider.of<ContactInfoState>(context).isGroup && Provider.of<ContactInfoState>(context).status != "Authenticated";
|
bool isGroupAndNotAuthenticated = Provider.of<ContactInfoState>(context).isGroup && Provider.of<ContactInfoState>(context).status != "Authenticated";
|
||||||
|
|
||||||
bool showEphemeralWarning = (isP2P && Provider.of<ContactInfoState>(context).savePeerHistory != "SaveHistory");
|
bool showEphemeralWarning = (isP2P && Provider.of<ContactInfoState>(context).savePeerHistory != "SaveHistory");
|
||||||
bool showOfflineWarning = Provider.of<ContactInfoState>(context).isOnline() == false;
|
bool showOfflineWarning = Provider.of<ContactInfoState>(context).isOnline() == false;
|
||||||
bool showMessageWarning = showEphemeralWarning || showOfflineWarning;
|
|
||||||
bool showSyncing = isGroupAndSyncing;
|
bool showSyncing = isGroupAndSyncing;
|
||||||
|
bool showMessageWarning = showEphemeralWarning || showOfflineWarning || showSyncing;
|
||||||
// Only load historical messages when the conversation is with a p2p contact OR the conversation is a server and *not* syncing.
|
// Only load historical messages when the conversation is with a p2p contact OR the conversation is a server and *not* syncing.
|
||||||
bool loadMessages = isP2P || (isGroupAndSynced || isGroupAndNotAuthenticated);
|
bool loadMessages = isP2P || (isGroupAndSynced || isGroupAndNotAuthenticated);
|
||||||
|
|
||||||
|
@ -37,18 +36,17 @@ class _MessageListState extends State<MessageList> {
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.all(5.0),
|
padding: EdgeInsets.all(5.0),
|
||||||
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
|
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
|
||||||
child: showSyncing ?
|
child: showSyncing
|
||||||
Text(AppLocalizations.of(context)!.serverNotSynced,
|
? Text(AppLocalizations.of(context)!.serverNotSynced, textAlign: TextAlign.center)
|
||||||
textAlign: TextAlign.center)
|
: showOfflineWarning
|
||||||
: showOfflineWarning
|
? Text(Provider.of<ContactInfoState>(context).isGroup ? AppLocalizations.of(context)!.serverConnectivityDisconnected : AppLocalizations.of(context)!.peerOfflineMessage,
|
||||||
? Text(Provider.of<ContactInfoState>(context).isGroup ? AppLocalizations.of(context)!.serverConnectivityDisconnected : AppLocalizations.of(context)!.peerOfflineMessage,
|
textAlign: TextAlign.center)
|
||||||
textAlign: TextAlign.center)
|
// Only show the ephemeral status for peer conversations, not for groups...
|
||||||
// Only show the ephemeral status for peer conversations, not for groups...
|
: (showEphemeralWarning
|
||||||
: (showEphemeralWarning
|
? Text(AppLocalizations.of(context)!.chatHistoryDefault, textAlign: TextAlign.center)
|
||||||
? Text(AppLocalizations.of(context)!.chatHistoryDefault, textAlign: TextAlign.center)
|
:
|
||||||
:
|
// We are not allowed to put null here, so put an empty text widge
|
||||||
// We are not allowed to put null here, so put an empty text widge
|
Text("")),
|
||||||
Text("")),
|
|
||||||
)),
|
)),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Scrollbar(
|
child: Scrollbar(
|
||||||
|
@ -63,30 +61,32 @@ class _MessageListState extends State<MessageList> {
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
image: AssetImage("assets/core/negative_heart_512px.png"),
|
image: AssetImage("assets/core/negative_heart_512px.png"),
|
||||||
colorFilter: ColorFilter.mode(Provider.of<Settings>(context).theme.hilightElementTextColor(), BlendMode.srcIn))),
|
colorFilter: ColorFilter.mode(Provider.of<Settings>(context).theme.hilightElementTextColor(), BlendMode.srcIn))),
|
||||||
// Don't load messages for syncing server...
|
// Don't load messages for syncing server...
|
||||||
child: loadMessages ? ListView.builder(
|
child: loadMessages
|
||||||
controller: ctrlr1,
|
? ListView.builder(
|
||||||
itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages,
|
controller: ctrlr1,
|
||||||
reverse: true, // NOTE: There seems to be a bug in flutter that corrects the mouse wheel scroll, but not the drag direction...
|
itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages,
|
||||||
itemBuilder: (itemBuilderContext, index) {
|
reverse: true, // NOTE: There seems to be a bug in flutter that corrects the mouse wheel scroll, but not the drag direction...
|
||||||
var trueIndex = Provider.of<ContactInfoState>(outerContext).totalMessages - index - 1;
|
itemBuilder: (itemBuilderContext, index) {
|
||||||
return ChangeNotifierProvider(
|
var trueIndex = Provider.of<ContactInfoState>(outerContext).totalMessages - index - 1;
|
||||||
key: ValueKey(trueIndex),
|
return ChangeNotifierProvider(
|
||||||
create: (x) => MessageState(
|
key: ValueKey(trueIndex),
|
||||||
context: itemBuilderContext,
|
create: (x) => MessageState(
|
||||||
profileOnion: Provider.of<ProfileInfoState>(outerContext, listen: false).onion,
|
context: itemBuilderContext,
|
||||||
// We don't want to listen for updates to the contact handle...
|
profileOnion: Provider.of<ProfileInfoState>(outerContext, listen: false).onion,
|
||||||
contactHandle: Provider.of<ContactInfoState>(x, listen: false).onion,
|
// We don't want to listen for updates to the contact handle...
|
||||||
messageIndex: trueIndex,
|
contactHandle: Provider.of<ContactInfoState>(x, listen: false).onion,
|
||||||
),
|
messageIndex: trueIndex,
|
||||||
builder: (bcontext, child) {
|
),
|
||||||
String idx = Provider.of<ContactInfoState>(outerContext).isGroup == true && Provider.of<MessageState>(bcontext).signature.isEmpty == false
|
builder: (bcontext, child) {
|
||||||
? Provider.of<MessageState>(bcontext).signature
|
String idx = Provider.of<ContactInfoState>(outerContext).isGroup == true && Provider.of<MessageState>(bcontext).signature.isEmpty == false
|
||||||
: trueIndex.toString();
|
? Provider.of<MessageState>(bcontext).signature
|
||||||
return RepaintBoundary(child: MessageRow(key: Provider.of<ContactInfoState>(bcontext).getMessageKey(idx)));
|
: trueIndex.toString();
|
||||||
});
|
return RepaintBoundary(child: MessageRow(key: Provider.of<ContactInfoState>(bcontext).getMessageKey(idx)));
|
||||||
},
|
});
|
||||||
) : null )))
|
},
|
||||||
|
)
|
||||||
|
: null)))
|
||||||
])));
|
])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,43 +30,29 @@ class _ProfileRowState extends State<ProfileRow> {
|
||||||
padding: const EdgeInsets.all(2.0), //border size
|
padding: const EdgeInsets.all(2.0), //border size
|
||||||
child: ProfileImage(
|
child: ProfileImage(
|
||||||
badgeCount: 0,
|
badgeCount: 0,
|
||||||
badgeColor: Provider
|
badgeColor: Provider.of<Settings>(context).theme.portraitProfileBadgeColor(),
|
||||||
.of<Settings>(context)
|
badgeTextColor: Provider.of<Settings>(context).theme.portraitProfileBadgeTextColor(),
|
||||||
.theme
|
|
||||||
.portraitProfileBadgeColor(),
|
|
||||||
badgeTextColor: Provider
|
|
||||||
.of<Settings>(context)
|
|
||||||
.theme
|
|
||||||
.portraitProfileBadgeTextColor(),
|
|
||||||
diameter: 64.0,
|
diameter: 64.0,
|
||||||
imagePath: profile.imagePath,
|
imagePath: profile.imagePath,
|
||||||
border: profile.isOnline ? Provider
|
border: profile.isOnline ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor() : Provider.of<Settings>(context).theme.portraitOfflineBorderColor())),
|
||||||
.of<Settings>(context)
|
|
||||||
.theme
|
|
||||||
.portraitOnlineBorderColor() : Provider
|
|
||||||
.of<Settings>(context)
|
|
||||||
.theme
|
|
||||||
.portraitOfflineBorderColor())),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
profile.nickname,
|
profile.nickname,
|
||||||
semanticsLabel: profile.nickname,
|
semanticsLabel: profile.nickname,
|
||||||
style: Provider
|
style: Provider.of<FlwtchState>(context).biggerFont,
|
||||||
.of<FlwtchState>(context)
|
softWrap: true,
|
||||||
.biggerFont,
|
overflow: TextOverflow.ellipsis,
|
||||||
softWrap: true,
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
ExcludeSemantics(
|
||||||
),
|
child: Text(
|
||||||
ExcludeSemantics(
|
profile.onion,
|
||||||
child: Text(
|
softWrap: true,
|
||||||
profile.onion,
|
overflow: TextOverflow.ellipsis,
|
||||||
softWrap: true,
|
))
|
||||||
overflow: TextOverflow.ellipsis,
|
],
|
||||||
))
|
)),
|
||||||
],
|
|
||||||
)),
|
|
||||||
IconButton(
|
IconButton(
|
||||||
enableFeedback: true,
|
enableFeedback: true,
|
||||||
tooltip: AppLocalizations.of(context)!.editProfile + " " + profile.nickname,
|
tooltip: AppLocalizations.of(context)!.editProfile + " " + profile.nickname,
|
||||||
|
@ -83,7 +69,7 @@ class _ProfileRowState extends State<ProfileRow> {
|
||||||
appState.selectedProfile = profile.onion;
|
appState.selectedProfile = profile.onion;
|
||||||
appState.selectedConversation = null;
|
appState.selectedConversation = null;
|
||||||
|
|
||||||
_pushContactList(profile, appState.isLandscape(context));//orientation == Orientation.landscape);
|
_pushContactList(profile, appState.isLandscape(context)); //orientation == Orientation.landscape);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
@ -94,19 +80,17 @@ class _ProfileRowState extends State<ProfileRow> {
|
||||||
MaterialPageRoute<void>(
|
MaterialPageRoute<void>(
|
||||||
settings: RouteSettings(name: "conversations"),
|
settings: RouteSettings(name: "conversations"),
|
||||||
builder: (BuildContext buildcontext) {
|
builder: (BuildContext buildcontext) {
|
||||||
return OrientationBuilder(
|
return OrientationBuilder(builder: (orientationBuilderContext, orientation) {
|
||||||
builder: (orientationBuilderContext, orientation) {
|
return MultiProvider(
|
||||||
return MultiProvider(
|
providers: [
|
||||||
providers: [
|
ChangeNotifierProvider<ProfileInfoState>.value(value: profile),
|
||||||
ChangeNotifierProvider<ProfileInfoState>.value(value: profile),
|
ChangeNotifierProvider<ContactListState>.value(value: profile.contactList),
|
||||||
ChangeNotifierProvider<ContactListState>.value(value: profile.contactList),
|
],
|
||||||
],
|
builder: (innercontext, widget) {
|
||||||
builder: (innercontext, widget) {
|
var appState = Provider.of<AppState>(context);
|
||||||
var appState = Provider.of<AppState>(context);
|
var settings = Provider.of<Settings>(context);
|
||||||
var settings = Provider.of<Settings>(context);
|
return settings.uiColumns(appState.isLandscape(innercontext)).length > 1 ? DoubleColumnView() : ContactsView();
|
||||||
return settings.uiColumns(appState.isLandscape(innercontext)).length > 1 ? DoubleColumnView() : ContactsView();
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
40
pubspec.lock
40
pubspec.lock
|
@ -42,7 +42,7 @@ packages:
|
||||||
name: charcode
|
name: charcode
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.2.0"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -70,7 +70,7 @@ packages:
|
||||||
name: dbus
|
name: dbus
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.1"
|
version: "0.5.2"
|
||||||
desktop_notifications:
|
desktop_notifications:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -91,14 +91,14 @@ packages:
|
||||||
name: ffi
|
name: ffi
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.1.2"
|
||||||
file:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file
|
name: file
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.1"
|
version: "6.1.2"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -125,7 +125,7 @@ packages:
|
||||||
name: glob
|
name: glob
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "2.0.1"
|
||||||
http:
|
http:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -189,20 +189,6 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
node_interop:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: node_interop
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.2.1"
|
|
||||||
node_io:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: node_io
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.1"
|
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -216,7 +202,7 @@ packages:
|
||||||
name: package_info_plus
|
name: package_info_plus
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.3"
|
||||||
package_info_plus_linux:
|
package_info_plus_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -244,14 +230,14 @@ 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.1"
|
version: "1.0.2"
|
||||||
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.1"
|
version: "1.0.2"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -265,7 +251,7 @@ packages:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.2"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -300,14 +286,14 @@ packages:
|
||||||
name: pedantic
|
name: pedantic
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.0"
|
version: "1.11.1"
|
||||||
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.1.0"
|
version: "4.2.0"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -382,7 +368,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.1"
|
version: "0.4.0"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -403,7 +389,7 @@ packages:
|
||||||
name: win32
|
name: win32
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.2.4"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 51 KiB |
|
@ -0,0 +1,92 @@
|
||||||
|
; USAGE: Run in ui/deploy, requires the output be in 'windows' directory
|
||||||
|
|
||||||
|
!include "MUI2.nsh"
|
||||||
|
|
||||||
|
; General settings ----------------------------
|
||||||
|
Name "Cwtch"
|
||||||
|
; !define MUI_BRANDINGTEXT "SIG Beta Ver. 1.0"
|
||||||
|
|
||||||
|
Unicode True
|
||||||
|
|
||||||
|
# define the name of the installer
|
||||||
|
Outfile "cwtch-installer.exe"
|
||||||
|
|
||||||
|
# For removing Start Menu shortcut in Windows 7
|
||||||
|
#RequestExecutionLevel user
|
||||||
|
RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on)
|
||||||
|
|
||||||
|
# define the directory to install to, the desktop in this case as specified
|
||||||
|
# by the predefined $DESKTOP variable
|
||||||
|
InstallDir "$PROGRAMFILES\Cwtch"
|
||||||
|
|
||||||
|
;Get installation folder from registry if available
|
||||||
|
InstallDirRegKey HKCU "Software\Cwtch" "installLocation"
|
||||||
|
|
||||||
|
; MUI Interface -----------------------------
|
||||||
|
|
||||||
|
!define MUI_INSTALLCOLORS "DFB9DE 281831"
|
||||||
|
|
||||||
|
; 128x128, 32bit
|
||||||
|
!define MUI_ICON "../runner/resources/knot_128.ico"
|
||||||
|
|
||||||
|
!define MUI_HEADERIMAGE
|
||||||
|
!define MUI_HEADERIMAGE_BITMAP "cwtch_title.bmp"
|
||||||
|
|
||||||
|
!define MUI_TEXTCOLOR "350052"
|
||||||
|
|
||||||
|
!define MUI_WELCOMEFINISHPAGE_BITMAP "brand_side.bmp"
|
||||||
|
!define MUI_WELCOMEFINISHPAGE_BITMAP_STRETCH NoStretchNoCrop
|
||||||
|
|
||||||
|
!define MUI_INSTFILESPAGE_COLORS "DFB9DE 281831"
|
||||||
|
!define MUI_INSTFILESPAGE_PROGRESSBAR "colored"
|
||||||
|
|
||||||
|
!define MUI_FINISHPAGE_NOAUTOCLOSE
|
||||||
|
|
||||||
|
|
||||||
|
ShowInstDetails show
|
||||||
|
|
||||||
|
; Pages --------
|
||||||
|
|
||||||
|
|
||||||
|
!define MUI_WELCOMEPAGE_TITLE "Welcome to the Cwtch installer"
|
||||||
|
!define MUI_WELCOMEPAGE_TEXT "Cwtch (pronounced: kutch) is a Welsh word roughly meaning 'a hug that creates a safe space'$\n$\n\
|
||||||
|
Cwtch is a platform for building consentful, decentralized, untrusted infrastructure using metadata resistant group communication applications. Currently there is a selfnamed instant messaging prototype app that is driving development and testing. Many Further apps are planned as the platform matures."
|
||||||
|
|
||||||
|
!define MUI_FINISHPAGE_TITLE "Enjoy Cwtch"
|
||||||
|
!define MUI_FINISHPAGE_RUN $INSTDIR/cwtch.exe
|
||||||
|
!define MUI_FINISHPAGE_TEXT "You can keep up-to-date on Cwtch and report any issues you have at https://cwtch.im"
|
||||||
|
!define MUI_FINISHPAGE_LINK "https://cwtch.im"
|
||||||
|
!define MUI_FINISHPAGE_LINK_LOCATION "https://cwtch.im"
|
||||||
|
!define MUI_FINISHPAGE_LINK_COLOR "D01972"
|
||||||
|
|
||||||
|
!insertmacro MUI_PAGE_WELCOME
|
||||||
|
!insertmacro MUI_PAGE_LICENSE "../../LICENSE"
|
||||||
|
!insertmacro MUI_PAGE_DIRECTORY
|
||||||
|
!insertmacro MUI_PAGE_INSTFILES
|
||||||
|
!insertmacro MUI_PAGE_FINISH
|
||||||
|
|
||||||
|
; Languages --------------------------------
|
||||||
|
|
||||||
|
!insertmacro MUI_LANGUAGE "English"
|
||||||
|
|
||||||
|
# default section
|
||||||
|
Section
|
||||||
|
|
||||||
|
# define the output path for this file
|
||||||
|
SetOutPath $INSTDIR
|
||||||
|
|
||||||
|
# define what to install and place it in the output path
|
||||||
|
# Filler for .sh to populate with contents of deploy/windows
|
||||||
|
#FILESLISTSTART
|
||||||
|
FILE /r "..\..\build\windows\runner\Release\"
|
||||||
|
#FILESLISTEND
|
||||||
|
|
||||||
|
|
||||||
|
# create a shortcut in the start menu programs directory
|
||||||
|
CreateDirectory "$SMPROGRAMS\Cwtch"
|
||||||
|
CreateShortcut "$SMPROGRAMS\Cwtch\Cwtch.lnk" "$INSTDIR\cwtch.exe" "" "$INSTDIR\cwtch.ico"
|
||||||
|
|
||||||
|
;Store installation folder
|
||||||
|
WriteRegStr HKCU "Software\Cwtch" "installLocation" $INSTDIR
|
||||||
|
|
||||||
|
SectionEnd
|
Binary file not shown.
After Width: | Height: | Size: 9.5 KiB |
|
@ -9,7 +9,14 @@ RunLoop::RunLoop() {}
|
||||||
RunLoop::~RunLoop() {}
|
RunLoop::~RunLoop() {}
|
||||||
|
|
||||||
void RunLoop::Run() {
|
void RunLoop::Run() {
|
||||||
bool keep_running = true;
|
// Fix/workaround for Windows high CPU usage
|
||||||
|
// https://github.com/flutter/flutter/issues/78517#issuecomment-814843107
|
||||||
|
MSG msg;
|
||||||
|
while (GetMessage(&msg, nullptr, 0, 0)) {
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessage(&msg);
|
||||||
|
}
|
||||||
|
/*bool keep_running = true;
|
||||||
TimePoint next_flutter_event_time = TimePoint::clock::now();
|
TimePoint next_flutter_event_time = TimePoint::clock::now();
|
||||||
while (keep_running) {
|
while (keep_running) {
|
||||||
std::chrono::nanoseconds wait_duration =
|
std::chrono::nanoseconds wait_duration =
|
||||||
|
@ -40,7 +47,7 @@ void RunLoop::Run() {
|
||||||
next_flutter_event_time =
|
next_flutter_event_time =
|
||||||
std::min(next_flutter_event_time, ProcessFlutterMessages());
|
std::min(next_flutter_event_time, ProcessFlutterMessages());
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunLoop::RegisterFlutterInstance(
|
void RunLoop::RegisterFlutterInstance(
|
||||||
|
|
Loading…
Reference in New Issue