Merge branch 'trunk' into tests
continuous-integration/drone/pr Build is failing
Details
continuous-integration/drone/pr Build is failing
Details
This commit is contained in:
commit
f101874ac2
|
@ -0,0 +1,119 @@
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: default
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch
|
||||||
|
image: cirrusci/flutter:dev
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /root/.pub-cache
|
||||||
|
commands:
|
||||||
|
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/tor
|
||||||
|
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/torrc
|
||||||
|
- chmod a+x tor
|
||||||
|
- git fetch --tags
|
||||||
|
- echo `git describe --tags` > VERSION
|
||||||
|
- echo `date +%G-%m-%d-%H-%M` > BUILDDATE
|
||||||
|
- flutter pub get
|
||||||
|
- mkdir deploy
|
||||||
|
|
||||||
|
#- name: quality
|
||||||
|
# image: golang
|
||||||
|
# volumes:
|
||||||
|
# - name: deps
|
||||||
|
# path: /go
|
||||||
|
# commands:
|
||||||
|
# - go list ./... | xargs go vet
|
||||||
|
# - go list ./... | xargs golint
|
||||||
|
# #Todo: fix all the lint errors and add `-set_exit_status` above to enforce linting
|
||||||
|
|
||||||
|
- name: build-linux
|
||||||
|
image: cirrusci/flutter:dev
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /root/.pub-cache
|
||||||
|
commands:
|
||||||
|
- flutter config --enable-linux-desktop
|
||||||
|
- apt-get update
|
||||||
|
- apt-get install -y --no-install-recommends cmake ninja-build clang build-essential pkg-config libgtk-3-dev liblzma-dev
|
||||||
|
- flutter build linux
|
||||||
|
- mkdir deploy/linux
|
||||||
|
- cp -r build/linux/x64/release/bundle/* deploy/linux
|
||||||
|
- cp /sdks/flutter/bin/cache/artifacts/engine/linux-x64/icudtl.dat deploy/linux
|
||||||
|
|
||||||
|
- name: build-android
|
||||||
|
image: cirrusci/flutter:dev
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /root/.pub-cache
|
||||||
|
commands:
|
||||||
|
- flutter build appbundle
|
||||||
|
# or build apk --split-per-abi ?
|
||||||
|
- mkdir deploy/android
|
||||||
|
- cp build/app/outputs/bundle/release/app-release.aab deploy/android
|
||||||
|
|
||||||
|
# Todo: gonna need more work on container
|
||||||
|
# https://flutter.dev/desktop
|
||||||
|
# requirements: Visual Studio 2019 (not to be confused with Visual Studio Code) with the “Desktop development with C++” workload installed, including all of its default components
|
||||||
|
#- name: build-windows
|
||||||
|
# image: cirrusci/flutter:dev
|
||||||
|
#- volumes:
|
||||||
|
# - name: deps
|
||||||
|
# path: /root/.pub-cache
|
||||||
|
# commands:
|
||||||
|
# - flutter config --enable-windows-desktop
|
||||||
|
# - flutter build windows
|
||||||
|
|
||||||
|
- name: deploy-buildfiles
|
||||||
|
image: kroniak/ssh-client
|
||||||
|
environment:
|
||||||
|
BUILDFILES_KEY:
|
||||||
|
from_secret: buildfiles_key
|
||||||
|
secrets: [gogs_account_token]
|
||||||
|
when:
|
||||||
|
event: push
|
||||||
|
status: [ success ]
|
||||||
|
commands:
|
||||||
|
- echo $BUILDFILES_KEY > ~/id_rsab64
|
||||||
|
- base64 -d ~/id_rsab64 > ~/id_rsa
|
||||||
|
- chmod 400 ~/id_rsa
|
||||||
|
- export DIR=flwtch-`cat VERSION`-`cat BUILDDATE`
|
||||||
|
- mv deploy $DIR
|
||||||
|
- cd $DIR
|
||||||
|
- find . -type f -exec sha256sum {} \; > ./../sha256s.txt
|
||||||
|
- mv ./../sha256s.txt .
|
||||||
|
- cd ..
|
||||||
|
# TODO: do deployment once files actaully compile
|
||||||
|
- scp -r -o StrictHostKeyChecking=no -i ~/id_rsa $DIR buildfiles@openprivacy.ca:/home/buildfiles/buildfiles/
|
||||||
|
|
||||||
|
- name: notify-email
|
||||||
|
image: drillster/drone-email
|
||||||
|
host: build.openprivacy.ca
|
||||||
|
port: 25
|
||||||
|
skip_verify: true
|
||||||
|
from: drone@openprivacy.ca
|
||||||
|
when:
|
||||||
|
status: [ failure ]
|
||||||
|
|
||||||
|
- name: notify-gogs
|
||||||
|
image: openpriv/drone-gogs
|
||||||
|
when:
|
||||||
|
event: pull_request
|
||||||
|
status: [ success, changed, failure ]
|
||||||
|
environment:
|
||||||
|
GOGS_ACCOUNT_TOKEN:
|
||||||
|
from_secret: gogs_account_token
|
||||||
|
gogs_url: https://git.openprivacy.ca
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
temp: {}
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
repo: flutter/flutter_app
|
||||||
|
branch: trunk
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- pull_request
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import '../errorHandler.dart';
|
||||||
import '../model.dart';
|
import '../model.dart';
|
||||||
import '../settings.dart';
|
import '../settings.dart';
|
||||||
|
|
||||||
|
@ -8,10 +9,12 @@ import '../settings.dart';
|
||||||
class CwtchNotifier {
|
class CwtchNotifier {
|
||||||
ProfileListState profileCN;
|
ProfileListState profileCN;
|
||||||
Settings settings;
|
Settings settings;
|
||||||
|
ErrorHandler error;
|
||||||
|
|
||||||
CwtchNotifier(ProfileListState pcn, Settings settingsCN) {
|
CwtchNotifier(ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN) {
|
||||||
profileCN = pcn;
|
profileCN = pcn;
|
||||||
settings = settingsCN;
|
settings = settingsCN;
|
||||||
|
error = errorCN;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleMessage(String type, dynamic data) {
|
void handleMessage(String type, dynamic data) {
|
||||||
|
@ -25,12 +28,13 @@ class CwtchNotifier {
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
case "PeerCreated":
|
case "PeerCreated":
|
||||||
print("xx peercreated");
|
print("xx peercreated $data");
|
||||||
profileCN.getProfile(data["ProfileOnion"]).contactList.add(ContactInfoState(
|
profileCN.getProfile(data["ProfileOnion"]).contactList.add(ContactInfoState(
|
||||||
profileOnion: data["ProfileOnion"],
|
profileOnion: data["ProfileOnion"],
|
||||||
onion: data["RemotePeer"],
|
onion: data["RemotePeer"],
|
||||||
nickname: data["nick"],
|
nickname: data["nick"],
|
||||||
status: data["status"],
|
status: data["status"],
|
||||||
|
imagePath: data["picture"],
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
case "PeerStateChange":
|
case "PeerStateChange":
|
||||||
|
@ -44,6 +48,10 @@ class CwtchNotifier {
|
||||||
contact.status = data["ConnectionState"];
|
contact.status = data["ConnectionState"];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "AppError":
|
||||||
|
print("New App Error: $data");
|
||||||
|
error.handleUpdate(data["Data"]);
|
||||||
|
break;
|
||||||
case "UpdateGlobalSettings":
|
case "UpdateGlobalSettings":
|
||||||
settings.handleUpdate(jsonDecode(data["Data"]));
|
settings.handleUpdate(jsonDecode(data["Data"]));
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -8,7 +8,6 @@ import 'package:path/path.dart' as path;
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
import 'package:flutter_app/cwtch/cwtch.dart';
|
import 'package:flutter_app/cwtch/cwtch.dart';
|
||||||
|
|
||||||
|
|
||||||
/////////////////////
|
/////////////////////
|
||||||
/// Cwtch API ///
|
/// Cwtch API ///
|
||||||
/////////////////////
|
/////////////////////
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
|
class ErrorHandler extends ChangeNotifier {
|
||||||
|
// General Error Types
|
||||||
|
static const String successErrorType = "success";
|
||||||
|
|
||||||
|
// Add Contact Specific Errors...
|
||||||
|
static const String addContactErrorPrefix = "addcontact";
|
||||||
|
static const String invalidImportStringErrorType = "invalid_import_string";
|
||||||
|
static const String contactAlreadyExistsErrorType = "contact_already_exists";
|
||||||
|
bool invalidImportStringError = false;
|
||||||
|
bool contactAlreadyExistsError = false;
|
||||||
|
bool explicitAddContactSuccess = false;
|
||||||
|
|
||||||
|
/// Called by the event bus.
|
||||||
|
handleUpdate(String error) {
|
||||||
|
var parts = error.split(".");
|
||||||
|
String prefix = parts[0];
|
||||||
|
String errorType = parts[1];
|
||||||
|
|
||||||
|
switch (prefix) {
|
||||||
|
case addContactErrorPrefix:
|
||||||
|
handleAddContactError(errorType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAddContactError(String errorType) {
|
||||||
|
// Reset add contact errors
|
||||||
|
invalidImportStringError = false;
|
||||||
|
contactAlreadyExistsError = false;
|
||||||
|
explicitAddContactSuccess = false;
|
||||||
|
|
||||||
|
switch (errorType) {
|
||||||
|
case invalidImportStringErrorType:
|
||||||
|
invalidImportStringError = true;
|
||||||
|
break;
|
||||||
|
case contactAlreadyExistsErrorType:
|
||||||
|
contactAlreadyExistsError = true;
|
||||||
|
break;
|
||||||
|
case successErrorType:
|
||||||
|
explicitAddContactSuccess = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
"builddate": "Aufgebaut auf: %2",
|
"builddate": "Aufgebaut auf: %2",
|
||||||
"bulletinsBtn": "Meldungen",
|
"bulletinsBtn": "Meldungen",
|
||||||
"chatBtn": "Chat",
|
"chatBtn": "Chat",
|
||||||
|
"contactAlreadyExists": "",
|
||||||
"conversationSettings": "",
|
"conversationSettings": "",
|
||||||
"copiedClipboardNotification": "in die Zwischenablage kopiert",
|
"copiedClipboardNotification": "in die Zwischenablage kopiert",
|
||||||
"copiedToClipboardNotification": "in die Zwischenablage kopiert",
|
"copiedToClipboardNotification": "in die Zwischenablage kopiert",
|
||||||
|
@ -56,6 +57,7 @@
|
||||||
"groupAddr": "Adresse",
|
"groupAddr": "Adresse",
|
||||||
"groupName": "Gruppenname",
|
"groupName": "Gruppenname",
|
||||||
"groupNameLabel": "Gruppenname",
|
"groupNameLabel": "Gruppenname",
|
||||||
|
"invalidImportString": "",
|
||||||
"invitation": "Einladung",
|
"invitation": "Einladung",
|
||||||
"invitationLabel": "Einladung",
|
"invitationLabel": "Einladung",
|
||||||
"inviteBtn": "Einladen",
|
"inviteBtn": "Einladen",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"builddate": "Built on: %2",
|
"builddate": "Built on: %2",
|
||||||
"bulletinsBtn": "Bulletins",
|
"bulletinsBtn": "Bulletins",
|
||||||
"chatBtn": "Chat",
|
"chatBtn": "Chat",
|
||||||
|
"contactAlreadyExists": "Contact Already Exists",
|
||||||
"conversationSettings": "Conversation Settings",
|
"conversationSettings": "Conversation Settings",
|
||||||
"copiedClipboardNotification": "Copied to clipboard",
|
"copiedClipboardNotification": "Copied to clipboard",
|
||||||
"copiedToClipboardNotification": "Copied to Clipboard",
|
"copiedToClipboardNotification": "Copied to Clipboard",
|
||||||
|
@ -56,6 +57,7 @@
|
||||||
"groupAddr": "Address",
|
"groupAddr": "Address",
|
||||||
"groupName": "Group name",
|
"groupName": "Group name",
|
||||||
"groupNameLabel": "Group Name",
|
"groupNameLabel": "Group Name",
|
||||||
|
"invalidImportString": "Invalid import string",
|
||||||
"invitation": "Invitation",
|
"invitation": "Invitation",
|
||||||
"invitationLabel": "Invitation",
|
"invitationLabel": "Invitation",
|
||||||
"inviteBtn": "Invite",
|
"inviteBtn": "Invite",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"builddate": "Basado en: %2",
|
"builddate": "Basado en: %2",
|
||||||
"bulletinsBtn": "Boletines",
|
"bulletinsBtn": "Boletines",
|
||||||
"chatBtn": "Chat",
|
"chatBtn": "Chat",
|
||||||
|
"contactAlreadyExists": "",
|
||||||
"conversationSettings": "",
|
"conversationSettings": "",
|
||||||
"copiedClipboardNotification": "Copiado al portapapeles",
|
"copiedClipboardNotification": "Copiado al portapapeles",
|
||||||
"copiedToClipboardNotification": "Copiado al portapapeles",
|
"copiedToClipboardNotification": "Copiado al portapapeles",
|
||||||
|
@ -56,6 +57,7 @@
|
||||||
"groupAddr": "Dirección",
|
"groupAddr": "Dirección",
|
||||||
"groupName": "Nombre del grupo",
|
"groupName": "Nombre del grupo",
|
||||||
"groupNameLabel": "Nombre del grupo",
|
"groupNameLabel": "Nombre del grupo",
|
||||||
|
"invalidImportString": "",
|
||||||
"invitation": "Invitación",
|
"invitation": "Invitación",
|
||||||
"invitationLabel": "Invitación",
|
"invitationLabel": "Invitación",
|
||||||
"inviteBtn": "Invitar",
|
"inviteBtn": "Invitar",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"builddate": "",
|
"builddate": "",
|
||||||
"bulletinsBtn": "Bulletins",
|
"bulletinsBtn": "Bulletins",
|
||||||
"chatBtn": "Discuter",
|
"chatBtn": "Discuter",
|
||||||
|
"contactAlreadyExists": "",
|
||||||
"conversationSettings": "",
|
"conversationSettings": "",
|
||||||
"copiedClipboardNotification": "Copié dans le presse-papier",
|
"copiedClipboardNotification": "Copié dans le presse-papier",
|
||||||
"copiedToClipboardNotification": "Copié dans le presse-papier",
|
"copiedToClipboardNotification": "Copié dans le presse-papier",
|
||||||
|
@ -56,6 +57,7 @@
|
||||||
"groupAddr": "",
|
"groupAddr": "",
|
||||||
"groupName": "",
|
"groupName": "",
|
||||||
"groupNameLabel": "Nom du groupe",
|
"groupNameLabel": "Nom du groupe",
|
||||||
|
"invalidImportString": "",
|
||||||
"invitation": "",
|
"invitation": "",
|
||||||
"invitationLabel": "Invitation",
|
"invitationLabel": "Invitation",
|
||||||
"inviteBtn": "Invitation",
|
"inviteBtn": "Invitation",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"builddate": "Costruito il: %2",
|
"builddate": "Costruito il: %2",
|
||||||
"bulletinsBtn": "Bollettini",
|
"bulletinsBtn": "Bollettini",
|
||||||
"chatBtn": "Chat",
|
"chatBtn": "Chat",
|
||||||
|
"contactAlreadyExists": "",
|
||||||
"conversationSettings": "",
|
"conversationSettings": "",
|
||||||
"copiedClipboardNotification": "Copiato negli Appunti",
|
"copiedClipboardNotification": "Copiato negli Appunti",
|
||||||
"copiedToClipboardNotification": "Copiato negli Appunti",
|
"copiedToClipboardNotification": "Copiato negli Appunti",
|
||||||
|
@ -56,6 +57,7 @@
|
||||||
"groupAddr": "Indirizzo",
|
"groupAddr": "Indirizzo",
|
||||||
"groupName": "Nome del gruppo",
|
"groupName": "Nome del gruppo",
|
||||||
"groupNameLabel": "Nome del gruppo",
|
"groupNameLabel": "Nome del gruppo",
|
||||||
|
"invalidImportString": "",
|
||||||
"invitation": "Invito",
|
"invitation": "Invito",
|
||||||
"invitationLabel": "Invito",
|
"invitationLabel": "Invito",
|
||||||
"inviteBtn": "Invitare",
|
"inviteBtn": "Invitare",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"builddate": "",
|
"builddate": "",
|
||||||
"bulletinsBtn": "Boletins",
|
"bulletinsBtn": "Boletins",
|
||||||
"chatBtn": "Chat",
|
"chatBtn": "Chat",
|
||||||
|
"contactAlreadyExists": "",
|
||||||
"conversationSettings": "",
|
"conversationSettings": "",
|
||||||
"copiedClipboardNotification": "Copiado",
|
"copiedClipboardNotification": "Copiado",
|
||||||
"copiedToClipboardNotification": "Copiado",
|
"copiedToClipboardNotification": "Copiado",
|
||||||
|
@ -56,6 +57,7 @@
|
||||||
"groupAddr": "",
|
"groupAddr": "",
|
||||||
"groupName": "",
|
"groupName": "",
|
||||||
"groupNameLabel": "Nome do Grupo",
|
"groupNameLabel": "Nome do Grupo",
|
||||||
|
"invalidImportString": "",
|
||||||
"invitation": "",
|
"invitation": "",
|
||||||
"invitationLabel": "Convite",
|
"invitationLabel": "Convite",
|
||||||
"inviteBtn": "Convidar",
|
"inviteBtn": "Convidar",
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_app/cwtch/ffi.dart';
|
import 'package:flutter_app/cwtch/ffi.dart';
|
||||||
import 'package:flutter_app/cwtch/gomobile.dart';
|
import 'package:flutter_app/cwtch/gomobile.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app/errorHandler.dart';
|
||||||
import 'package:flutter_app/settings.dart';
|
import 'package:flutter_app/settings.dart';
|
||||||
import 'package:flutter_app/views/triplecolview.dart';
|
import 'package:flutter_app/views/triplecolview.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
@ -16,6 +17,7 @@ import 'opaque.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
var globalSettings = Settings(Locale("en", ''), Opaque.dark);
|
var globalSettings = Settings(Locale("en", ''), Opaque.dark);
|
||||||
|
var globalErrorHandler = ErrorHandler();
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
LicenseRegistry.addLicense(() => licenses());
|
LicenseRegistry.addLicense(() => licenses());
|
||||||
|
@ -46,7 +48,7 @@ class FlwtchState extends State<Flwtch> {
|
||||||
cwtchInit = false;
|
cwtchInit = false;
|
||||||
|
|
||||||
profs = ProfileListState();
|
profs = ProfileListState();
|
||||||
var cwtchNotifier = new CwtchNotifier(profs, globalSettings);
|
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler);
|
||||||
|
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
cwtch = CwtchGomobile(cwtchNotifier);
|
cwtch = CwtchGomobile(cwtchNotifier);
|
||||||
|
@ -63,6 +65,7 @@ class FlwtchState extends State<Flwtch> {
|
||||||
appStatus = AppModel(cwtch: cwtch);
|
appStatus = AppModel(cwtch: cwtch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChangeNotifierProvider<ErrorHandler> getErrorHandlerProvider() => ChangeNotifierProvider.value(value: globalErrorHandler);
|
||||||
ChangeNotifierProvider<Settings> getSettingsProvider() => ChangeNotifierProvider.value(value: globalSettings);
|
ChangeNotifierProvider<Settings> getSettingsProvider() => ChangeNotifierProvider.value(value: globalSettings);
|
||||||
Provider<FlwtchState> getFlwtchStateProvider() => Provider<FlwtchState>(create: (_) => this);
|
Provider<FlwtchState> getFlwtchStateProvider() => Provider<FlwtchState>(create: (_) => this);
|
||||||
ChangeNotifierProvider<ProfileListState> getProfileListProvider() => ChangeNotifierProvider(create: (context) => profs);
|
ChangeNotifierProvider<ProfileListState> getProfileListProvider() => ChangeNotifierProvider(create: (context) => profs);
|
||||||
|
@ -72,7 +75,7 @@ class FlwtchState extends State<Flwtch> {
|
||||||
//appStatus = AppModel(cwtch: cwtch);
|
//appStatus = AppModel(cwtch: cwtch);
|
||||||
|
|
||||||
return MultiProvider(
|
return MultiProvider(
|
||||||
providers: [getFlwtchStateProvider(), getProfileListProvider(), getSettingsProvider()],
|
providers: [getFlwtchStateProvider(), getProfileListProvider(), getSettingsProvider(), getErrorHandlerProvider()],
|
||||||
builder: (context, widget) {
|
builder: (context, widget) {
|
||||||
Provider.of<Settings>(context).initPackageInfo();
|
Provider.of<Settings>(context).initPackageInfo();
|
||||||
return Consumer<Settings>(
|
return Consumer<Settings>(
|
||||||
|
|
|
@ -192,7 +192,19 @@ class ContactInfoState extends ChangeNotifier {
|
||||||
int _unreadMessages = 0;
|
int _unreadMessages = 0;
|
||||||
int _totalMessages = 0;
|
int _totalMessages = 0;
|
||||||
|
|
||||||
ContactInfoState({this.profileOnion, this.onion, nickname = "", isGroup = false, isInvitation = false, isBlocked = false, status = "", imagePath = "", savePeerHistory = "DeleteHistoryConfirmed", numMessages = 0, numUnread = 0,}) {
|
ContactInfoState({
|
||||||
|
this.profileOnion,
|
||||||
|
this.onion,
|
||||||
|
nickname = "",
|
||||||
|
isGroup = false,
|
||||||
|
isInvitation = false,
|
||||||
|
isBlocked = false,
|
||||||
|
status = "",
|
||||||
|
imagePath = "",
|
||||||
|
savePeerHistory = "DeleteHistoryConfirmed",
|
||||||
|
numMessages = 0,
|
||||||
|
numUnread = 0,
|
||||||
|
}) {
|
||||||
this._nickname = nickname;
|
this._nickname = nickname;
|
||||||
this._isGroup = isGroup;
|
this._isGroup = isGroup;
|
||||||
this._isInvitation = isInvitation;
|
this._isInvitation = isInvitation;
|
||||||
|
|
|
@ -7,6 +7,8 @@ import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
|
||||||
import 'opaque.dart';
|
import 'opaque.dart';
|
||||||
|
|
||||||
|
const TapirGroupsExperiment = "tapir-groups-experiment";
|
||||||
|
|
||||||
/// Settings govern the *Globally* relevant settings like Locale, Theme and Experiments.
|
/// Settings govern the *Globally* relevant settings like Locale, Theme and Experiments.
|
||||||
/// We also provide access to the version information here as it is also accessed from the
|
/// We also provide access to the version information here as it is also accessed from the
|
||||||
/// Settings Pane.
|
/// Settings Pane.
|
||||||
|
|
|
@ -1,12 +1,33 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'dart:convert';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/scheduler.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_app/errorHandler.dart';
|
||||||
|
import 'package:flutter_app/settings.dart';
|
||||||
|
import 'package:flutter_app/widgets/buttontextfield.dart';
|
||||||
|
import 'package:flutter_app/widgets/cwtchlabel.dart';
|
||||||
|
import 'package:flutter_app/widgets/textfield.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import '../main.dart';
|
||||||
|
import '../model.dart';
|
||||||
|
|
||||||
|
/// Add Contact View is the one-stop shop for adding public keys to a Profiles contact list.
|
||||||
|
/// We support both Peers and Groups (experiment-pending).
|
||||||
|
/// NOTE: This view makes use of the global Error Handler to receive events from the Cwtch Library (for validating
|
||||||
|
/// error states caused by incorrect import string or duplicate requests to add a specific contact)
|
||||||
class AddContactView extends StatefulWidget {
|
class AddContactView extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
_AddContactViewState createState() => _AddContactViewState();
|
_AddContactViewState createState() => _AddContactViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AddContactViewState extends State<AddContactView> {
|
class _AddContactViewState extends State<AddContactView> {
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
final ctrlrOnion = TextEditingController(text: "");
|
||||||
|
final ctrlrContact = TextEditingController(text: "");
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -18,16 +39,119 @@ class _AddContactViewState extends State<AddContactView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildForm() {
|
Widget _buildForm() {
|
||||||
return Center(
|
ctrlrOnion.text = Provider.of<ProfileInfoState>(context).onion;
|
||||||
child: Wrap(
|
/// We display a different number of tabs dependening on the experiment setup
|
||||||
direction: Axis.vertical,
|
bool groupsEnabled = Provider.of<Settings>(context).experimentsEnabled && Provider.of<Settings>(context).experiments[TapirGroupsExperiment];
|
||||||
spacing: 20.0,
|
return Consumer<ErrorHandler>(builder: (context, globalErrorHandler, child) {
|
||||||
runSpacing: 20.0,
|
return DefaultTabController(
|
||||||
children: <Widget>[
|
length: groupsEnabled ? 3 : 1,
|
||||||
Text(AppLocalizations.of(context).profileName),
|
child: Column(children: [
|
||||||
Text("peer handle or group invite or server bundle"), //todo
|
(groupsEnabled ? getTabBarWithGroups() : getTabBarWithAddPeerOnly()),
|
||||||
Text(AppLocalizations.of(context).createGroupBtn),
|
Expanded(
|
||||||
|
child: TabBarView(
|
||||||
|
children: (groupsEnabled ? [addPeerTab(), addGroupTab(), joinGroupTab()] : [addPeerTab()]),
|
||||||
|
)),
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _copyOnion() {
|
||||||
|
Clipboard.setData(new ClipboardData(text: Provider.of<ProfileInfoState>(context, listen: false).onion));
|
||||||
|
// TODO Toast
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Tab Bar with only the Add Peer Tab
|
||||||
|
TabBar getTabBarWithAddPeerOnly() {
|
||||||
|
return TabBar(
|
||||||
|
tabs: [
|
||||||
|
Tab(
|
||||||
|
icon: Icon(Icons.person_add_rounded),
|
||||||
|
text: AppLocalizations.of(context).addPeer,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
));
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The full tab bar with Join and Add Groups
|
||||||
|
TabBar getTabBarWithGroups() {
|
||||||
|
return TabBar(
|
||||||
|
tabs: [
|
||||||
|
Tab(
|
||||||
|
icon: Icon(Icons.person_add_rounded),
|
||||||
|
text: AppLocalizations.of(context).addPeer,
|
||||||
|
),
|
||||||
|
Tab(icon: Icon(Icons.group), text: AppLocalizations.of(context).createGroup),
|
||||||
|
Tab(icon: Icon(Icons.group_add), text: AppLocalizations.of(context).joinGroup),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Add Peer Tab allows a peer to add a specific non-group peer to their contact lists
|
||||||
|
/// We also provide a convenient way to copy their onion.
|
||||||
|
Widget addPeerTab() {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.all(30),
|
||||||
|
padding: EdgeInsets.all(20),
|
||||||
|
child: Form(
|
||||||
|
autovalidateMode: AutovalidateMode.always,
|
||||||
|
key: _formKey,
|
||||||
|
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
|
CwtchLabel(label: AppLocalizations.of(context).profileOnionLabel),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
CwtchButtonTextField(
|
||||||
|
controller: ctrlrOnion,
|
||||||
|
onPressed: _copyOnion,
|
||||||
|
icon: Icon(Icons.copy),
|
||||||
|
tooltip: AppLocalizations.of(context).copyBtn,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
CwtchLabel(label: AppLocalizations.of(context).pasteAddressToAddContact),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
CwtchTextField(
|
||||||
|
controller: ctrlrContact,
|
||||||
|
validator: (value) {
|
||||||
|
if (value == "") {
|
||||||
|
return null;
|
||||||
|
} if (globalErrorHandler.invalidImportStringError) {
|
||||||
|
return AppLocalizations.of(context).invalidImportString;
|
||||||
|
} else if (globalErrorHandler.contactAlreadyExistsError) {
|
||||||
|
return AppLocalizations.of(context).contactAlreadyExists;
|
||||||
|
} else if (globalErrorHandler.explicitAddContactSuccess) {
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
onChanged: (String peerAddr) async {
|
||||||
|
var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
|
||||||
|
final setPeerAttribute = {
|
||||||
|
"EventType": "AddContact",
|
||||||
|
"Data": {"ImportString": peerAddr},
|
||||||
|
};
|
||||||
|
final setPeerAttributeJson = jsonEncode(setPeerAttribute);
|
||||||
|
Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(profileOnion, setPeerAttributeJson);
|
||||||
|
|
||||||
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
|
if (globalErrorHandler.explicitAddContactSuccess) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
])));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO Add Group Pane
|
||||||
|
Widget addGroupTab() {
|
||||||
|
return Icon(Icons.group_add);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO Join Group Pane
|
||||||
|
Widget joinGroupTab() {
|
||||||
|
return Icon(Icons.group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_app/widgets/contactrow.dart';
|
import 'package:flutter_app/widgets/contactrow.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import '../errorHandler.dart';
|
||||||
import '../main.dart';
|
import '../main.dart';
|
||||||
import 'addcontactview.dart';
|
import 'addcontactview.dart';
|
||||||
import '../model.dart';
|
import '../model.dart';
|
||||||
|
@ -48,9 +49,11 @@ class _ContactsViewState extends State<ContactsView> {
|
||||||
|
|
||||||
void _pushAddContact() {
|
void _pushAddContact() {
|
||||||
Navigator.of(context).push(MaterialPageRoute<void>(
|
Navigator.of(context).push(MaterialPageRoute<void>(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext bcontext) {
|
||||||
return Provider(
|
return MultiProvider(
|
||||||
create: (_) => Provider.of<FlwtchState>(context),
|
providers: [
|
||||||
|
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context)),
|
||||||
|
],
|
||||||
child: AddContactView(),
|
child: AddContactView(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -94,12 +94,12 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
||||||
children: [
|
children: [
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
title: Text(AppLocalizations.of(context).enableGroups, style: TextStyle(color: settings.current().mainTextColor())),
|
title: Text(AppLocalizations.of(context).enableGroups, style: TextStyle(color: settings.current().mainTextColor())),
|
||||||
value: settings.experiments.containsKey("tapir-groups-experiment") && settings.experiments["tapir-groups-experiment"],
|
value: settings.experiments.containsKey(TapirGroupsExperiment) && settings.experiments[TapirGroupsExperiment],
|
||||||
onChanged: (bool value) {
|
onChanged: (bool value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
settings.enableExperiment("tapir-groups-experiment");
|
settings.enableExperiment(TapirGroupsExperiment);
|
||||||
} else {
|
} else {
|
||||||
settings.disableExperiment("tapir-groups-experiment");
|
settings.disableExperiment(TapirGroupsExperiment);
|
||||||
}
|
}
|
||||||
// Save Settings...
|
// Save Settings...
|
||||||
saveSettings(context);
|
saveSettings(context);
|
||||||
|
|
|
@ -81,8 +81,18 @@ class _MessageViewState extends State<MessageView> {
|
||||||
onPressed: _sendMessage,
|
onPressed: _sendMessage,
|
||||||
),
|
),
|
||||||
Row(children: <Widget>[
|
Row(children: <Widget>[
|
||||||
SizedBox(width: 45, child: ElevatedButton(child: Icon(Icons.emoji_emotions_outlined, color: Opaque.current().mainTextColor()), onPressed: placeHolder,)),
|
SizedBox(
|
||||||
SizedBox(width: 45, child: ElevatedButton(child: Icon(Icons.attach_file, color: Opaque.current().mainTextColor()), onPressed: placeHolder,)),
|
width: 45,
|
||||||
|
child: ElevatedButton(
|
||||||
|
child: Icon(Icons.emoji_emotions_outlined, color: Opaque.current().mainTextColor()),
|
||||||
|
onPressed: placeHolder,
|
||||||
|
)),
|
||||||
|
SizedBox(
|
||||||
|
width: 45,
|
||||||
|
child: ElevatedButton(
|
||||||
|
child: Icon(Icons.attach_file, color: Opaque.current().mainTextColor()),
|
||||||
|
onPressed: placeHolder,
|
||||||
|
)),
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
|
|
|
@ -23,4 +23,4 @@ class _MessageListState extends State<MessageList> {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,16 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../settings.dart';
|
import '../settings.dart';
|
||||||
|
|
||||||
|
doNothing(String x) {}
|
||||||
|
|
||||||
// Provides a styled Text Field for use in Form Widgets.
|
// Provides a styled Text Field for use in Form Widgets.
|
||||||
// Callers must provide a text controller, label helper text and a validator.
|
// Callers must provide a text controller, label helper text and a validator.
|
||||||
class CwtchTextField extends StatefulWidget {
|
class CwtchTextField extends StatefulWidget {
|
||||||
CwtchTextField({this.controller, this.labelText, this.validator});
|
CwtchTextField({this.controller, this.labelText, this.validator, this.onChanged = doNothing});
|
||||||
final TextEditingController controller;
|
final TextEditingController controller;
|
||||||
final String labelText;
|
final String labelText;
|
||||||
final FormFieldValidator validator;
|
final FormFieldValidator validator;
|
||||||
|
final Function(String) onChanged;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_CwtchTextFieldState createState() => _CwtchTextFieldState();
|
_CwtchTextFieldState createState() => _CwtchTextFieldState();
|
||||||
|
@ -21,6 +24,7 @@ class _CwtchTextFieldState extends State<CwtchTextField> {
|
||||||
return TextFormField(
|
return TextFormField(
|
||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
validator: widget.validator,
|
validator: widget.validator,
|
||||||
|
onChanged: widget.onChanged,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: widget.labelText,
|
labelText: widget.labelText,
|
||||||
labelStyle: TextStyle(color: theme.current().mainTextColor(), backgroundColor: theme.current().textfieldBackgroundColor()),
|
labelStyle: TextStyle(color: theme.current().mainTextColor(), backgroundColor: theme.current().textfieldBackgroundColor()),
|
||||||
|
|
Loading…
Reference in New Issue