mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-09-01 08:54:08 +00:00
commit
d19f4ce3c9
139 changed files with 42166 additions and 2690 deletions
|
|
@ -1,5 +1,12 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.5.0
|
||||||
|
|
||||||
|
- New: Update to Signal's new PQC-ready key agreement PQXDH
|
||||||
|
- New: Contact labels
|
||||||
|
- Fix: Multiple bug fixes
|
||||||
|
- Fix: Multiple black screens
|
||||||
|
|
||||||
## 0.4.0
|
## 0.4.0
|
||||||
|
|
||||||
- New: Encrypted Cloud Backup of Memories
|
- New: Encrypted Cloud Backup of Memories
|
||||||
|
|
|
||||||
21
lib/app.dart
21
lib/app.dart
|
|
@ -89,15 +89,17 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||||
Locale('de', ''),
|
Locale('de', ''),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
final settings = context.watch<SettingsChangeProvider>();
|
||||||
|
|
||||||
if (widget.storageError) {
|
if (widget.storageError) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
localizationsDelegates: localizationsDelegates,
|
localizationsDelegates: localizationsDelegates,
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
supportedLocales: supportedLocales,
|
supportedLocales: supportedLocales,
|
||||||
title: 'twonly',
|
title: 'twonly',
|
||||||
theme: lightTheme,
|
theme: getLightTheme(settings.primaryColor),
|
||||||
darkTheme: darkTheme,
|
darkTheme: getDarkTheme(settings.primaryColor),
|
||||||
themeMode: context.read<SettingsChangeProvider>().themeMode,
|
themeMode: settings.themeMode,
|
||||||
home: const CriticalErrorView(),
|
home: const CriticalErrorView(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -108,9 +110,9 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
supportedLocales: supportedLocales,
|
supportedLocales: supportedLocales,
|
||||||
title: 'twonly',
|
title: 'twonly',
|
||||||
theme: lightTheme,
|
theme: getLightTheme(settings.primaryColor),
|
||||||
darkTheme: darkTheme,
|
darkTheme: getDarkTheme(settings.primaryColor),
|
||||||
themeMode: context.read<SettingsChangeProvider>().themeMode,
|
themeMode: settings.themeMode,
|
||||||
home: const RecoveryView(),
|
home: const RecoveryView(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -121,9 +123,9 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
supportedLocales: supportedLocales,
|
supportedLocales: supportedLocales,
|
||||||
title: 'twonly',
|
title: 'twonly',
|
||||||
theme: lightTheme,
|
theme: getLightTheme(settings.primaryColor),
|
||||||
darkTheme: darkTheme,
|
darkTheme: getDarkTheme(settings.primaryColor),
|
||||||
themeMode: context.read<SettingsChangeProvider>().themeMode,
|
themeMode: settings.themeMode,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -161,6 +163,7 @@ class _AppMainWidgetState extends State<AppMainWidget> {
|
||||||
initAsync();
|
initAsync();
|
||||||
|
|
||||||
void handleShareLink(Uri uri) {
|
void handleShareLink(Uri uri) {
|
||||||
|
HomeViewState.pendingSharedLink = uri;
|
||||||
routerProvider.go(Routes.home);
|
routerProvider.go(Routes.home);
|
||||||
HomeViewState.streamHomeViewPageIndex.add(1);
|
HomeViewState.streamHomeViewPageIndex.add(1);
|
||||||
HomeViewState.streamSharedLink.add(uri);
|
HomeViewState.streamSharedLink.add(uri);
|
||||||
|
|
|
||||||
57
lib/core/bridge/wrapper/signal.dart
Normal file
57
lib/core/bridge/wrapper/signal.dart
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
// This file is automatically generated, so please do not edit it.
|
||||||
|
// @generated by `flutter_rust_bridge`@ 2.12.0.
|
||||||
|
|
||||||
|
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||||
|
|
||||||
|
import '../../frb_generated.dart';
|
||||||
|
import '../../signal/engine.dart';
|
||||||
|
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||||
|
|
||||||
|
class RustSignal {
|
||||||
|
const RustSignal();
|
||||||
|
|
||||||
|
static Future<Uint8List> decrypt({
|
||||||
|
required String name,
|
||||||
|
required int deviceId,
|
||||||
|
required List<int> ciphertext,
|
||||||
|
}) => RustLib.instance.api.crateBridgeWrapperSignalRustSignalDecrypt(
|
||||||
|
name: name,
|
||||||
|
deviceId: deviceId,
|
||||||
|
ciphertext: ciphertext,
|
||||||
|
);
|
||||||
|
|
||||||
|
static Future<Uint8List> encrypt({
|
||||||
|
required String name,
|
||||||
|
required int deviceId,
|
||||||
|
required List<int> plaintext,
|
||||||
|
}) => RustLib.instance.api.crateBridgeWrapperSignalRustSignalEncrypt(
|
||||||
|
name: name,
|
||||||
|
deviceId: deviceId,
|
||||||
|
plaintext: plaintext,
|
||||||
|
);
|
||||||
|
|
||||||
|
static Future<FrbPreKeyBundle> generateBundle() =>
|
||||||
|
RustLib.instance.api.crateBridgeWrapperSignalRustSignalGenerateBundle();
|
||||||
|
|
||||||
|
static Future<List<FrbPqcPreKey>> generatePqcPrekeys() => RustLib.instance.api
|
||||||
|
.crateBridgeWrapperSignalRustSignalGeneratePqcPrekeys();
|
||||||
|
|
||||||
|
static Future<void> processPrekeyBundle({
|
||||||
|
required String name,
|
||||||
|
required int deviceId,
|
||||||
|
required FrbPreKeyBundle bundle,
|
||||||
|
}) => RustLib.instance.api
|
||||||
|
.crateBridgeWrapperSignalRustSignalProcessPrekeyBundle(
|
||||||
|
name: name,
|
||||||
|
deviceId: deviceId,
|
||||||
|
bundle: bundle,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is RustSignal && runtimeType == other.runtimeType;
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ import 'bridge/callbacks/user_discovery.dart';
|
||||||
import 'bridge/wrapper.dart';
|
import 'bridge/wrapper.dart';
|
||||||
import 'bridge/wrapper/backup.dart';
|
import 'bridge/wrapper/backup.dart';
|
||||||
import 'bridge/wrapper/key_manager.dart';
|
import 'bridge/wrapper/key_manager.dart';
|
||||||
|
import 'bridge/wrapper/signal.dart';
|
||||||
import 'bridge/wrapper/user_discovery.dart';
|
import 'bridge/wrapper/user_discovery.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
@ -18,6 +19,7 @@ import 'frb_generated.io.dart'
|
||||||
import 'keys/backup_password_keys.dart';
|
import 'keys/backup_password_keys.dart';
|
||||||
import 'lib.dart';
|
import 'lib.dart';
|
||||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||||
|
import 'signal/engine.dart';
|
||||||
|
|
||||||
/// Main entrypoint of the Rust API
|
/// Main entrypoint of the Rust API
|
||||||
class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
||||||
|
|
@ -76,7 +78,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
||||||
String get codegenVersion => '2.12.0';
|
String get codegenVersion => '2.12.0';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get rustContentHash => 1788847092;
|
int get rustContentHash => 1109927244;
|
||||||
|
|
||||||
static const kDefaultExternalLibraryLoaderConfig =
|
static const kDefaultExternalLibraryLoaderConfig =
|
||||||
ExternalLibraryLoaderConfig(
|
ExternalLibraryLoaderConfig(
|
||||||
|
|
@ -259,6 +261,29 @@ abstract class RustLibApi extends BaseApi {
|
||||||
required List<int> record,
|
required List<int> record,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Future<Uint8List> crateBridgeWrapperSignalRustSignalDecrypt({
|
||||||
|
required String name,
|
||||||
|
required int deviceId,
|
||||||
|
required List<int> ciphertext,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<Uint8List> crateBridgeWrapperSignalRustSignalEncrypt({
|
||||||
|
required String name,
|
||||||
|
required int deviceId,
|
||||||
|
required List<int> plaintext,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<FrbPreKeyBundle> crateBridgeWrapperSignalRustSignalGenerateBundle();
|
||||||
|
|
||||||
|
Future<List<FrbPqcPreKey>>
|
||||||
|
crateBridgeWrapperSignalRustSignalGeneratePqcPrekeys();
|
||||||
|
|
||||||
|
Future<void> crateBridgeWrapperSignalRustSignalProcessPrekeyBundle({
|
||||||
|
required String name,
|
||||||
|
required int deviceId,
|
||||||
|
required FrbPreKeyBundle bundle,
|
||||||
|
});
|
||||||
|
|
||||||
Future<List<Uint8List>> crateBridgeWrapperRustUtilsGenerateShares({
|
Future<List<Uint8List>> crateBridgeWrapperRustUtilsGenerateShares({
|
||||||
required List<int> secret,
|
required List<int> secret,
|
||||||
required int total,
|
required int total,
|
||||||
|
|
@ -1651,6 +1676,183 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
argNames: ["signedPreKeyId", "record"],
|
argNames: ["signedPreKeyId", "record"],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Uint8List> crateBridgeWrapperSignalRustSignalDecrypt({
|
||||||
|
required String name,
|
||||||
|
required int deviceId,
|
||||||
|
required List<int> ciphertext,
|
||||||
|
}) {
|
||||||
|
return handler.executeNormal(
|
||||||
|
NormalTask(
|
||||||
|
callFfi: (port_) {
|
||||||
|
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||||
|
sse_encode_String(name, serializer);
|
||||||
|
sse_encode_u_32(deviceId, serializer);
|
||||||
|
sse_encode_list_prim_u_8_loose(ciphertext, serializer);
|
||||||
|
pdeCallFfi(
|
||||||
|
generalizedFrbRustBinding,
|
||||||
|
serializer,
|
||||||
|
funcId: 32,
|
||||||
|
port: port_,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
codec: SseCodec(
|
||||||
|
decodeSuccessData: sse_decode_list_prim_u_8_strict,
|
||||||
|
decodeErrorData: sse_decode_AnyhowException,
|
||||||
|
),
|
||||||
|
constMeta: kCrateBridgeWrapperSignalRustSignalDecryptConstMeta,
|
||||||
|
argValues: [name, deviceId, ciphertext],
|
||||||
|
apiImpl: this,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskConstMeta get kCrateBridgeWrapperSignalRustSignalDecryptConstMeta =>
|
||||||
|
const TaskConstMeta(
|
||||||
|
debugName: "rust_signal_decrypt",
|
||||||
|
argNames: ["name", "deviceId", "ciphertext"],
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Uint8List> crateBridgeWrapperSignalRustSignalEncrypt({
|
||||||
|
required String name,
|
||||||
|
required int deviceId,
|
||||||
|
required List<int> plaintext,
|
||||||
|
}) {
|
||||||
|
return handler.executeNormal(
|
||||||
|
NormalTask(
|
||||||
|
callFfi: (port_) {
|
||||||
|
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||||
|
sse_encode_String(name, serializer);
|
||||||
|
sse_encode_u_32(deviceId, serializer);
|
||||||
|
sse_encode_list_prim_u_8_loose(plaintext, serializer);
|
||||||
|
pdeCallFfi(
|
||||||
|
generalizedFrbRustBinding,
|
||||||
|
serializer,
|
||||||
|
funcId: 33,
|
||||||
|
port: port_,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
codec: SseCodec(
|
||||||
|
decodeSuccessData: sse_decode_list_prim_u_8_strict,
|
||||||
|
decodeErrorData: sse_decode_AnyhowException,
|
||||||
|
),
|
||||||
|
constMeta: kCrateBridgeWrapperSignalRustSignalEncryptConstMeta,
|
||||||
|
argValues: [name, deviceId, plaintext],
|
||||||
|
apiImpl: this,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskConstMeta get kCrateBridgeWrapperSignalRustSignalEncryptConstMeta =>
|
||||||
|
const TaskConstMeta(
|
||||||
|
debugName: "rust_signal_encrypt",
|
||||||
|
argNames: ["name", "deviceId", "plaintext"],
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<FrbPreKeyBundle> crateBridgeWrapperSignalRustSignalGenerateBundle() {
|
||||||
|
return handler.executeNormal(
|
||||||
|
NormalTask(
|
||||||
|
callFfi: (port_) {
|
||||||
|
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||||
|
pdeCallFfi(
|
||||||
|
generalizedFrbRustBinding,
|
||||||
|
serializer,
|
||||||
|
funcId: 34,
|
||||||
|
port: port_,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
codec: SseCodec(
|
||||||
|
decodeSuccessData: sse_decode_frb_pre_key_bundle,
|
||||||
|
decodeErrorData: sse_decode_AnyhowException,
|
||||||
|
),
|
||||||
|
constMeta: kCrateBridgeWrapperSignalRustSignalGenerateBundleConstMeta,
|
||||||
|
argValues: [],
|
||||||
|
apiImpl: this,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskConstMeta
|
||||||
|
get kCrateBridgeWrapperSignalRustSignalGenerateBundleConstMeta =>
|
||||||
|
const TaskConstMeta(
|
||||||
|
debugName: "rust_signal_generate_bundle",
|
||||||
|
argNames: [],
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<FrbPqcPreKey>>
|
||||||
|
crateBridgeWrapperSignalRustSignalGeneratePqcPrekeys() {
|
||||||
|
return handler.executeNormal(
|
||||||
|
NormalTask(
|
||||||
|
callFfi: (port_) {
|
||||||
|
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||||
|
pdeCallFfi(
|
||||||
|
generalizedFrbRustBinding,
|
||||||
|
serializer,
|
||||||
|
funcId: 35,
|
||||||
|
port: port_,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
codec: SseCodec(
|
||||||
|
decodeSuccessData: sse_decode_list_frb_pqc_pre_key,
|
||||||
|
decodeErrorData: sse_decode_AnyhowException,
|
||||||
|
),
|
||||||
|
constMeta:
|
||||||
|
kCrateBridgeWrapperSignalRustSignalGeneratePqcPrekeysConstMeta,
|
||||||
|
argValues: [],
|
||||||
|
apiImpl: this,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskConstMeta
|
||||||
|
get kCrateBridgeWrapperSignalRustSignalGeneratePqcPrekeysConstMeta =>
|
||||||
|
const TaskConstMeta(
|
||||||
|
debugName: "rust_signal_generate_pqc_prekeys",
|
||||||
|
argNames: [],
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> crateBridgeWrapperSignalRustSignalProcessPrekeyBundle({
|
||||||
|
required String name,
|
||||||
|
required int deviceId,
|
||||||
|
required FrbPreKeyBundle bundle,
|
||||||
|
}) {
|
||||||
|
return handler.executeNormal(
|
||||||
|
NormalTask(
|
||||||
|
callFfi: (port_) {
|
||||||
|
final serializer = SseSerializer(generalizedFrbRustBinding);
|
||||||
|
sse_encode_String(name, serializer);
|
||||||
|
sse_encode_u_32(deviceId, serializer);
|
||||||
|
sse_encode_box_autoadd_frb_pre_key_bundle(bundle, serializer);
|
||||||
|
pdeCallFfi(
|
||||||
|
generalizedFrbRustBinding,
|
||||||
|
serializer,
|
||||||
|
funcId: 36,
|
||||||
|
port: port_,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
codec: SseCodec(
|
||||||
|
decodeSuccessData: sse_decode_unit,
|
||||||
|
decodeErrorData: sse_decode_AnyhowException,
|
||||||
|
),
|
||||||
|
constMeta:
|
||||||
|
kCrateBridgeWrapperSignalRustSignalProcessPrekeyBundleConstMeta,
|
||||||
|
argValues: [name, deviceId, bundle],
|
||||||
|
apiImpl: this,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskConstMeta
|
||||||
|
get kCrateBridgeWrapperSignalRustSignalProcessPrekeyBundleConstMeta =>
|
||||||
|
const TaskConstMeta(
|
||||||
|
debugName: "rust_signal_process_prekey_bundle",
|
||||||
|
argNames: ["name", "deviceId", "bundle"],
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Uint8List>> crateBridgeWrapperRustUtilsGenerateShares({
|
Future<List<Uint8List>> crateBridgeWrapperRustUtilsGenerateShares({
|
||||||
required List<int> secret,
|
required List<int> secret,
|
||||||
|
|
@ -1667,7 +1869,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 32,
|
funcId: 37,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -1702,7 +1904,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 33,
|
funcId: 38,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -1738,7 +1940,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 34,
|
funcId: 39,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -1775,7 +1977,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 35,
|
funcId: 40,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -1813,7 +2015,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 36,
|
funcId: 41,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -1851,7 +2053,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 37,
|
funcId: 42,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -1889,7 +2091,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 38,
|
funcId: 43,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -1928,7 +2130,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 39,
|
funcId: 44,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -1967,7 +2169,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 40,
|
funcId: 45,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -2012,7 +2214,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 41,
|
funcId: 46,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -2064,7 +2266,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 42,
|
funcId: 47,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -2105,7 +2307,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 43,
|
funcId: 48,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -2143,7 +2345,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 44,
|
funcId: 49,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -2181,7 +2383,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 45,
|
funcId: 50,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -2219,7 +2421,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 46,
|
funcId: 51,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -2257,7 +2459,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 47,
|
funcId: 52,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -2299,7 +2501,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 48,
|
funcId: 53,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -2339,7 +2541,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
pdeCallFfi(
|
pdeCallFfi(
|
||||||
generalizedFrbRustBinding,
|
generalizedFrbRustBinding,
|
||||||
serializer,
|
serializer,
|
||||||
funcId: 49,
|
funcId: 54,
|
||||||
port: port_,
|
port: port_,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -2978,6 +3180,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
return dco_decode_backup_password_keys(raw);
|
return dco_decode_backup_password_keys(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPreKeyBundle dco_decode_box_autoadd_frb_pre_key_bundle(dynamic raw) {
|
||||||
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
|
return dco_decode_frb_pre_key_bundle(raw);
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 dco_decode_box_autoadd_i_64(dynamic raw) {
|
PlatformInt64 dco_decode_box_autoadd_i_64(dynamic raw) {
|
||||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
|
|
@ -2996,6 +3204,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
return dco_decode_other_promotion(raw);
|
return dco_decode_other_promotion(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int dco_decode_box_autoadd_u_32(dynamic raw) {
|
||||||
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
|
return raw as int;
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
UserDiscoveryStoreFlutter dco_decode_box_autoadd_user_discovery_store_flutter(
|
UserDiscoveryStoreFlutter dco_decode_box_autoadd_user_discovery_store_flutter(
|
||||||
dynamic raw,
|
dynamic raw,
|
||||||
|
|
@ -3021,6 +3235,42 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
return FlutterUserDiscovery();
|
return FlutterUserDiscovery();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPqcPreKey dco_decode_frb_pqc_pre_key(dynamic raw) {
|
||||||
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
|
final arr = raw as List<dynamic>;
|
||||||
|
if (arr.length != 5)
|
||||||
|
throw Exception('unexpected arr length: expect 5 but see ${arr.length}');
|
||||||
|
return FrbPqcPreKey(
|
||||||
|
eccPreKeyId: dco_decode_u_32(arr[0]),
|
||||||
|
eccPreKey: dco_decode_list_prim_u_8_strict(arr[1]),
|
||||||
|
kyberPreKeyId: dco_decode_u_32(arr[2]),
|
||||||
|
kyberPreKey: dco_decode_list_prim_u_8_strict(arr[3]),
|
||||||
|
kyberPreKeySignature: dco_decode_list_prim_u_8_strict(arr[4]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPreKeyBundle dco_decode_frb_pre_key_bundle(dynamic raw) {
|
||||||
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
|
final arr = raw as List<dynamic>;
|
||||||
|
if (arr.length != 11)
|
||||||
|
throw Exception('unexpected arr length: expect 11 but see ${arr.length}');
|
||||||
|
return FrbPreKeyBundle(
|
||||||
|
registrationId: dco_decode_u_32(arr[0]),
|
||||||
|
deviceId: dco_decode_u_32(arr[1]),
|
||||||
|
preKeyId: dco_decode_opt_box_autoadd_u_32(arr[2]),
|
||||||
|
preKeyPublic: dco_decode_opt_list_prim_u_8_strict(arr[3]),
|
||||||
|
signedPreKeyId: dco_decode_u_32(arr[4]),
|
||||||
|
signedPreKeyPublic: dco_decode_list_prim_u_8_strict(arr[5]),
|
||||||
|
signedPreKeySignature: dco_decode_list_prim_u_8_strict(arr[6]),
|
||||||
|
kyberPreKeyId: dco_decode_u_32(arr[7]),
|
||||||
|
kyberPreKeyPublic: dco_decode_list_prim_u_8_strict(arr[8]),
|
||||||
|
kyberPreKeySignature: dco_decode_list_prim_u_8_strict(arr[9]),
|
||||||
|
identityKey: dco_decode_list_prim_u_8_strict(arr[10]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 dco_decode_i_64(dynamic raw) {
|
PlatformInt64 dco_decode_i_64(dynamic raw) {
|
||||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
|
|
@ -3045,6 +3295,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
return dcoDecodeI64(raw);
|
return dcoDecodeI64(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<FrbPqcPreKey> dco_decode_list_frb_pqc_pre_key(dynamic raw) {
|
||||||
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
|
return (raw as List<dynamic>).map(dco_decode_frb_pqc_pre_key).toList();
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
List<Uint8List> dco_decode_list_list_prim_u_8_strict(dynamic raw) {
|
List<Uint8List> dco_decode_list_list_prim_u_8_strict(dynamic raw) {
|
||||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
|
|
@ -3096,6 +3352,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
return raw == null ? null : dco_decode_box_autoadd_i_64(raw);
|
return raw == null ? null : dco_decode_box_autoadd_i_64(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int? dco_decode_opt_box_autoadd_u_32(dynamic raw) {
|
||||||
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
|
return raw == null ? null : dco_decode_box_autoadd_u_32(raw);
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
List<Uint8List>? dco_decode_opt_list_list_prim_u_8_strict(dynamic raw) {
|
List<Uint8List>? dco_decode_opt_list_list_prim_u_8_strict(dynamic raw) {
|
||||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
|
|
@ -3200,6 +3462,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
return RustKeyManager();
|
return RustKeyManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
RustSignal dco_decode_rust_signal(dynamic raw) {
|
||||||
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
|
final arr = raw as List<dynamic>;
|
||||||
|
if (arr.length != 0)
|
||||||
|
throw Exception('unexpected arr length: expect 0 but see ${arr.length}');
|
||||||
|
return RustSignal();
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
RustUtils dco_decode_rust_utils(dynamic raw) {
|
RustUtils dco_decode_rust_utils(dynamic raw) {
|
||||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
|
|
@ -3347,6 +3618,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
return (sse_decode_backup_password_keys(deserializer));
|
return (sse_decode_backup_password_keys(deserializer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPreKeyBundle sse_decode_box_autoadd_frb_pre_key_bundle(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
return (sse_decode_frb_pre_key_bundle(deserializer));
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 sse_decode_box_autoadd_i_64(SseDeserializer deserializer) {
|
PlatformInt64 sse_decode_box_autoadd_i_64(SseDeserializer deserializer) {
|
||||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
|
@ -3367,6 +3646,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
return (sse_decode_other_promotion(deserializer));
|
return (sse_decode_other_promotion(deserializer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int sse_decode_box_autoadd_u_32(SseDeserializer deserializer) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
return (sse_decode_u_32(deserializer));
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
UserDiscoveryStoreFlutter sse_decode_box_autoadd_user_discovery_store_flutter(
|
UserDiscoveryStoreFlutter sse_decode_box_autoadd_user_discovery_store_flutter(
|
||||||
SseDeserializer deserializer,
|
SseDeserializer deserializer,
|
||||||
|
|
@ -3391,6 +3676,58 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
return FlutterUserDiscovery();
|
return FlutterUserDiscovery();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPqcPreKey sse_decode_frb_pqc_pre_key(SseDeserializer deserializer) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
var var_eccPreKeyId = sse_decode_u_32(deserializer);
|
||||||
|
var var_eccPreKey = sse_decode_list_prim_u_8_strict(deserializer);
|
||||||
|
var var_kyberPreKeyId = sse_decode_u_32(deserializer);
|
||||||
|
var var_kyberPreKey = sse_decode_list_prim_u_8_strict(deserializer);
|
||||||
|
var var_kyberPreKeySignature = sse_decode_list_prim_u_8_strict(
|
||||||
|
deserializer,
|
||||||
|
);
|
||||||
|
return FrbPqcPreKey(
|
||||||
|
eccPreKeyId: var_eccPreKeyId,
|
||||||
|
eccPreKey: var_eccPreKey,
|
||||||
|
kyberPreKeyId: var_kyberPreKeyId,
|
||||||
|
kyberPreKey: var_kyberPreKey,
|
||||||
|
kyberPreKeySignature: var_kyberPreKeySignature,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPreKeyBundle sse_decode_frb_pre_key_bundle(SseDeserializer deserializer) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
var var_registrationId = sse_decode_u_32(deserializer);
|
||||||
|
var var_deviceId = sse_decode_u_32(deserializer);
|
||||||
|
var var_preKeyId = sse_decode_opt_box_autoadd_u_32(deserializer);
|
||||||
|
var var_preKeyPublic = sse_decode_opt_list_prim_u_8_strict(deserializer);
|
||||||
|
var var_signedPreKeyId = sse_decode_u_32(deserializer);
|
||||||
|
var var_signedPreKeyPublic = sse_decode_list_prim_u_8_strict(deserializer);
|
||||||
|
var var_signedPreKeySignature = sse_decode_list_prim_u_8_strict(
|
||||||
|
deserializer,
|
||||||
|
);
|
||||||
|
var var_kyberPreKeyId = sse_decode_u_32(deserializer);
|
||||||
|
var var_kyberPreKeyPublic = sse_decode_list_prim_u_8_strict(deserializer);
|
||||||
|
var var_kyberPreKeySignature = sse_decode_list_prim_u_8_strict(
|
||||||
|
deserializer,
|
||||||
|
);
|
||||||
|
var var_identityKey = sse_decode_list_prim_u_8_strict(deserializer);
|
||||||
|
return FrbPreKeyBundle(
|
||||||
|
registrationId: var_registrationId,
|
||||||
|
deviceId: var_deviceId,
|
||||||
|
preKeyId: var_preKeyId,
|
||||||
|
preKeyPublic: var_preKeyPublic,
|
||||||
|
signedPreKeyId: var_signedPreKeyId,
|
||||||
|
signedPreKeyPublic: var_signedPreKeyPublic,
|
||||||
|
signedPreKeySignature: var_signedPreKeySignature,
|
||||||
|
kyberPreKeyId: var_kyberPreKeyId,
|
||||||
|
kyberPreKeyPublic: var_kyberPreKeyPublic,
|
||||||
|
kyberPreKeySignature: var_kyberPreKeySignature,
|
||||||
|
identityKey: var_identityKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer) {
|
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer) {
|
||||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
|
@ -3411,6 +3748,20 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
return deserializer.buffer.getPlatformInt64();
|
return deserializer.buffer.getPlatformInt64();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<FrbPqcPreKey> sse_decode_list_frb_pqc_pre_key(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
|
||||||
|
var len_ = sse_decode_i_32(deserializer);
|
||||||
|
var ans_ = <FrbPqcPreKey>[];
|
||||||
|
for (var idx_ = 0; idx_ < len_; ++idx_) {
|
||||||
|
ans_.add(sse_decode_frb_pqc_pre_key(deserializer));
|
||||||
|
}
|
||||||
|
return ans_;
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
List<Uint8List> sse_decode_list_list_prim_u_8_strict(
|
List<Uint8List> sse_decode_list_list_prim_u_8_strict(
|
||||||
SseDeserializer deserializer,
|
SseDeserializer deserializer,
|
||||||
|
|
@ -3503,6 +3854,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int? sse_decode_opt_box_autoadd_u_32(SseDeserializer deserializer) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
|
||||||
|
if (sse_decode_bool(deserializer)) {
|
||||||
|
return (sse_decode_box_autoadd_u_32(deserializer));
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
List<Uint8List>? sse_decode_opt_list_list_prim_u_8_strict(
|
List<Uint8List>? sse_decode_opt_list_list_prim_u_8_strict(
|
||||||
SseDeserializer deserializer,
|
SseDeserializer deserializer,
|
||||||
|
|
@ -3613,6 +3975,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
return RustKeyManager();
|
return RustKeyManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
RustSignal sse_decode_rust_signal(SseDeserializer deserializer) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
return RustSignal();
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
RustUtils sse_decode_rust_utils(SseDeserializer deserializer) {
|
RustUtils sse_decode_rust_utils(SseDeserializer deserializer) {
|
||||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
|
@ -3946,6 +4314,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
sse_encode_backup_password_keys(self, serializer);
|
sse_encode_backup_password_keys(self, serializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_box_autoadd_frb_pre_key_bundle(
|
||||||
|
FrbPreKeyBundle self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
sse_encode_frb_pre_key_bundle(self, serializer);
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_box_autoadd_i_64(
|
void sse_encode_box_autoadd_i_64(
|
||||||
PlatformInt64 self,
|
PlatformInt64 self,
|
||||||
|
|
@ -3973,6 +4350,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
sse_encode_other_promotion(self, serializer);
|
sse_encode_other_promotion(self, serializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
sse_encode_u_32(self, serializer);
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_box_autoadd_user_discovery_store_flutter(
|
void sse_encode_box_autoadd_user_discovery_store_flutter(
|
||||||
UserDiscoveryStoreFlutter self,
|
UserDiscoveryStoreFlutter self,
|
||||||
|
|
@ -3999,6 +4382,35 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_frb_pqc_pre_key(FrbPqcPreKey self, SseSerializer serializer) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
sse_encode_u_32(self.eccPreKeyId, serializer);
|
||||||
|
sse_encode_list_prim_u_8_strict(self.eccPreKey, serializer);
|
||||||
|
sse_encode_u_32(self.kyberPreKeyId, serializer);
|
||||||
|
sse_encode_list_prim_u_8_strict(self.kyberPreKey, serializer);
|
||||||
|
sse_encode_list_prim_u_8_strict(self.kyberPreKeySignature, serializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_frb_pre_key_bundle(
|
||||||
|
FrbPreKeyBundle self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
sse_encode_u_32(self.registrationId, serializer);
|
||||||
|
sse_encode_u_32(self.deviceId, serializer);
|
||||||
|
sse_encode_opt_box_autoadd_u_32(self.preKeyId, serializer);
|
||||||
|
sse_encode_opt_list_prim_u_8_strict(self.preKeyPublic, serializer);
|
||||||
|
sse_encode_u_32(self.signedPreKeyId, serializer);
|
||||||
|
sse_encode_list_prim_u_8_strict(self.signedPreKeyPublic, serializer);
|
||||||
|
sse_encode_list_prim_u_8_strict(self.signedPreKeySignature, serializer);
|
||||||
|
sse_encode_u_32(self.kyberPreKeyId, serializer);
|
||||||
|
sse_encode_list_prim_u_8_strict(self.kyberPreKeyPublic, serializer);
|
||||||
|
sse_encode_list_prim_u_8_strict(self.kyberPreKeySignature, serializer);
|
||||||
|
sse_encode_list_prim_u_8_strict(self.identityKey, serializer);
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer) {
|
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer) {
|
||||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
|
@ -4018,6 +4430,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
serializer.buffer.putPlatformInt64(self);
|
serializer.buffer.putPlatformInt64(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_list_frb_pqc_pre_key(
|
||||||
|
List<FrbPqcPreKey> self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
sse_encode_i_32(self.length, serializer);
|
||||||
|
for (final item in self) {
|
||||||
|
sse_encode_frb_pqc_pre_key(item, serializer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_list_list_prim_u_8_strict(
|
void sse_encode_list_list_prim_u_8_strict(
|
||||||
List<Uint8List> self,
|
List<Uint8List> self,
|
||||||
|
|
@ -4112,6 +4536,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_opt_box_autoadd_u_32(int? self, SseSerializer serializer) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
|
||||||
|
sse_encode_bool(self != null, serializer);
|
||||||
|
if (self != null) {
|
||||||
|
sse_encode_box_autoadd_u_32(self, serializer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_opt_list_list_prim_u_8_strict(
|
void sse_encode_opt_list_list_prim_u_8_strict(
|
||||||
List<Uint8List>? self,
|
List<Uint8List>? self,
|
||||||
|
|
@ -4222,6 +4656,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_rust_signal(RustSignal self, SseSerializer serializer) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_rust_utils(RustUtils self, SseSerializer serializer) {
|
void sse_encode_rust_utils(RustUtils self, SseSerializer serializer) {
|
||||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import 'bridge/callbacks/user_discovery.dart';
|
||||||
import 'bridge/wrapper.dart';
|
import 'bridge/wrapper.dart';
|
||||||
import 'bridge/wrapper/backup.dart';
|
import 'bridge/wrapper/backup.dart';
|
||||||
import 'bridge/wrapper/key_manager.dart';
|
import 'bridge/wrapper/key_manager.dart';
|
||||||
|
import 'bridge/wrapper/signal.dart';
|
||||||
import 'bridge/wrapper/user_discovery.dart';
|
import 'bridge/wrapper/user_discovery.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
@ -17,6 +18,7 @@ import 'frb_generated.dart';
|
||||||
import 'keys/backup_password_keys.dart';
|
import 'keys/backup_password_keys.dart';
|
||||||
import 'lib.dart';
|
import 'lib.dart';
|
||||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart';
|
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart';
|
||||||
|
import 'signal/engine.dart';
|
||||||
|
|
||||||
abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
RustLibApiImplPlatform({
|
RustLibApiImplPlatform({
|
||||||
|
|
@ -130,6 +132,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
BackupPasswordKeys dco_decode_box_autoadd_backup_password_keys(dynamic raw);
|
BackupPasswordKeys dco_decode_box_autoadd_backup_password_keys(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPreKeyBundle dco_decode_box_autoadd_frb_pre_key_bundle(dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 dco_decode_box_autoadd_i_64(dynamic raw);
|
PlatformInt64 dco_decode_box_autoadd_i_64(dynamic raw);
|
||||||
|
|
||||||
|
|
@ -139,6 +144,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
OtherPromotion dco_decode_box_autoadd_other_promotion(dynamic raw);
|
OtherPromotion dco_decode_box_autoadd_other_promotion(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int dco_decode_box_autoadd_u_32(dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
UserDiscoveryStoreFlutter dco_decode_box_autoadd_user_discovery_store_flutter(
|
UserDiscoveryStoreFlutter dco_decode_box_autoadd_user_discovery_store_flutter(
|
||||||
dynamic raw,
|
dynamic raw,
|
||||||
|
|
@ -152,6 +160,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
FlutterUserDiscovery dco_decode_flutter_user_discovery(dynamic raw);
|
FlutterUserDiscovery dco_decode_flutter_user_discovery(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPqcPreKey dco_decode_frb_pqc_pre_key(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPreKeyBundle dco_decode_frb_pre_key_bundle(dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 dco_decode_i_64(dynamic raw);
|
PlatformInt64 dco_decode_i_64(dynamic raw);
|
||||||
|
|
||||||
|
|
@ -161,6 +175,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 dco_decode_isize(dynamic raw);
|
PlatformInt64 dco_decode_isize(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<FrbPqcPreKey> dco_decode_list_frb_pqc_pre_key(dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
List<Uint8List> dco_decode_list_list_prim_u_8_strict(dynamic raw);
|
List<Uint8List> dco_decode_list_list_prim_u_8_strict(dynamic raw);
|
||||||
|
|
||||||
|
|
@ -186,6 +203,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64? dco_decode_opt_box_autoadd_i_64(dynamic raw);
|
PlatformInt64? dco_decode_opt_box_autoadd_i_64(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int? dco_decode_opt_box_autoadd_u_32(dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
List<Uint8List>? dco_decode_opt_list_list_prim_u_8_strict(dynamic raw);
|
List<Uint8List>? dco_decode_opt_list_list_prim_u_8_strict(dynamic raw);
|
||||||
|
|
||||||
|
|
@ -220,6 +240,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
RustKeyManager dco_decode_rust_key_manager(dynamic raw);
|
RustKeyManager dco_decode_rust_key_manager(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
RustSignal dco_decode_rust_signal(dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
RustUtils dco_decode_rust_utils(dynamic raw);
|
RustUtils dco_decode_rust_utils(dynamic raw);
|
||||||
|
|
||||||
|
|
@ -288,6 +311,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseDeserializer deserializer,
|
SseDeserializer deserializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPreKeyBundle sse_decode_box_autoadd_frb_pre_key_bundle(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 sse_decode_box_autoadd_i_64(SseDeserializer deserializer);
|
PlatformInt64 sse_decode_box_autoadd_i_64(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
|
@ -299,6 +327,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseDeserializer deserializer,
|
SseDeserializer deserializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int sse_decode_box_autoadd_u_32(SseDeserializer deserializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
UserDiscoveryStoreFlutter sse_decode_box_autoadd_user_discovery_store_flutter(
|
UserDiscoveryStoreFlutter sse_decode_box_autoadd_user_discovery_store_flutter(
|
||||||
SseDeserializer deserializer,
|
SseDeserializer deserializer,
|
||||||
|
|
@ -314,6 +345,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseDeserializer deserializer,
|
SseDeserializer deserializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPqcPreKey sse_decode_frb_pqc_pre_key(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPreKeyBundle sse_decode_frb_pre_key_bundle(SseDeserializer deserializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer);
|
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
|
@ -323,6 +360,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 sse_decode_isize(SseDeserializer deserializer);
|
PlatformInt64 sse_decode_isize(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<FrbPqcPreKey> sse_decode_list_frb_pqc_pre_key(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
List<Uint8List> sse_decode_list_list_prim_u_8_strict(
|
List<Uint8List> sse_decode_list_list_prim_u_8_strict(
|
||||||
SseDeserializer deserializer,
|
SseDeserializer deserializer,
|
||||||
|
|
@ -356,6 +398,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64? sse_decode_opt_box_autoadd_i_64(SseDeserializer deserializer);
|
PlatformInt64? sse_decode_opt_box_autoadd_i_64(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int? sse_decode_opt_box_autoadd_u_32(SseDeserializer deserializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
List<Uint8List>? sse_decode_opt_list_list_prim_u_8_strict(
|
List<Uint8List>? sse_decode_opt_list_list_prim_u_8_strict(
|
||||||
SseDeserializer deserializer,
|
SseDeserializer deserializer,
|
||||||
|
|
@ -400,6 +445,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
RustKeyManager sse_decode_rust_key_manager(SseDeserializer deserializer);
|
RustKeyManager sse_decode_rust_key_manager(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
RustSignal sse_decode_rust_signal(SseDeserializer deserializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
RustUtils sse_decode_rust_utils(SseDeserializer deserializer);
|
RustUtils sse_decode_rust_utils(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
|
@ -561,6 +609,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseSerializer serializer,
|
SseSerializer serializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_box_autoadd_frb_pre_key_bundle(
|
||||||
|
FrbPreKeyBundle self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_box_autoadd_i_64(
|
void sse_encode_box_autoadd_i_64(
|
||||||
PlatformInt64 self,
|
PlatformInt64 self,
|
||||||
|
|
@ -579,6 +633,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseSerializer serializer,
|
SseSerializer serializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_box_autoadd_user_discovery_store_flutter(
|
void sse_encode_box_autoadd_user_discovery_store_flutter(
|
||||||
UserDiscoveryStoreFlutter self,
|
UserDiscoveryStoreFlutter self,
|
||||||
|
|
@ -597,6 +654,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseSerializer serializer,
|
SseSerializer serializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_frb_pqc_pre_key(FrbPqcPreKey self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_frb_pre_key_bundle(
|
||||||
|
FrbPreKeyBundle self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer);
|
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer);
|
||||||
|
|
||||||
|
|
@ -606,6 +672,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_isize(PlatformInt64 self, SseSerializer serializer);
|
void sse_encode_isize(PlatformInt64 self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_list_frb_pqc_pre_key(
|
||||||
|
List<FrbPqcPreKey> self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_list_list_prim_u_8_strict(
|
void sse_encode_list_list_prim_u_8_strict(
|
||||||
List<Uint8List> self,
|
List<Uint8List> self,
|
||||||
|
|
@ -648,6 +720,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseSerializer serializer,
|
SseSerializer serializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_opt_box_autoadd_u_32(int? self, SseSerializer serializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_opt_list_list_prim_u_8_strict(
|
void sse_encode_opt_list_list_prim_u_8_strict(
|
||||||
List<Uint8List>? self,
|
List<Uint8List>? self,
|
||||||
|
|
@ -708,6 +783,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseSerializer serializer,
|
SseSerializer serializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_rust_signal(RustSignal self, SseSerializer serializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_rust_utils(RustUtils self, SseSerializer serializer);
|
void sse_encode_rust_utils(RustUtils self, SseSerializer serializer);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import 'bridge/callbacks/user_discovery.dart';
|
||||||
import 'bridge/wrapper.dart';
|
import 'bridge/wrapper.dart';
|
||||||
import 'bridge/wrapper/backup.dart';
|
import 'bridge/wrapper/backup.dart';
|
||||||
import 'bridge/wrapper/key_manager.dart';
|
import 'bridge/wrapper/key_manager.dart';
|
||||||
|
import 'bridge/wrapper/signal.dart';
|
||||||
import 'bridge/wrapper/user_discovery.dart';
|
import 'bridge/wrapper/user_discovery.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
@ -19,6 +20,7 @@ import 'frb_generated.dart';
|
||||||
import 'keys/backup_password_keys.dart';
|
import 'keys/backup_password_keys.dart';
|
||||||
import 'lib.dart';
|
import 'lib.dart';
|
||||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_web.dart';
|
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_web.dart';
|
||||||
|
import 'signal/engine.dart';
|
||||||
|
|
||||||
abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
RustLibApiImplPlatform({
|
RustLibApiImplPlatform({
|
||||||
|
|
@ -132,6 +134,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
BackupPasswordKeys dco_decode_box_autoadd_backup_password_keys(dynamic raw);
|
BackupPasswordKeys dco_decode_box_autoadd_backup_password_keys(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPreKeyBundle dco_decode_box_autoadd_frb_pre_key_bundle(dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 dco_decode_box_autoadd_i_64(dynamic raw);
|
PlatformInt64 dco_decode_box_autoadd_i_64(dynamic raw);
|
||||||
|
|
||||||
|
|
@ -141,6 +146,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
OtherPromotion dco_decode_box_autoadd_other_promotion(dynamic raw);
|
OtherPromotion dco_decode_box_autoadd_other_promotion(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int dco_decode_box_autoadd_u_32(dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
UserDiscoveryStoreFlutter dco_decode_box_autoadd_user_discovery_store_flutter(
|
UserDiscoveryStoreFlutter dco_decode_box_autoadd_user_discovery_store_flutter(
|
||||||
dynamic raw,
|
dynamic raw,
|
||||||
|
|
@ -154,6 +162,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
FlutterUserDiscovery dco_decode_flutter_user_discovery(dynamic raw);
|
FlutterUserDiscovery dco_decode_flutter_user_discovery(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPqcPreKey dco_decode_frb_pqc_pre_key(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPreKeyBundle dco_decode_frb_pre_key_bundle(dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 dco_decode_i_64(dynamic raw);
|
PlatformInt64 dco_decode_i_64(dynamic raw);
|
||||||
|
|
||||||
|
|
@ -163,6 +177,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 dco_decode_isize(dynamic raw);
|
PlatformInt64 dco_decode_isize(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<FrbPqcPreKey> dco_decode_list_frb_pqc_pre_key(dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
List<Uint8List> dco_decode_list_list_prim_u_8_strict(dynamic raw);
|
List<Uint8List> dco_decode_list_list_prim_u_8_strict(dynamic raw);
|
||||||
|
|
||||||
|
|
@ -188,6 +205,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64? dco_decode_opt_box_autoadd_i_64(dynamic raw);
|
PlatformInt64? dco_decode_opt_box_autoadd_i_64(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int? dco_decode_opt_box_autoadd_u_32(dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
List<Uint8List>? dco_decode_opt_list_list_prim_u_8_strict(dynamic raw);
|
List<Uint8List>? dco_decode_opt_list_list_prim_u_8_strict(dynamic raw);
|
||||||
|
|
||||||
|
|
@ -222,6 +242,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
RustKeyManager dco_decode_rust_key_manager(dynamic raw);
|
RustKeyManager dco_decode_rust_key_manager(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
RustSignal dco_decode_rust_signal(dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
RustUtils dco_decode_rust_utils(dynamic raw);
|
RustUtils dco_decode_rust_utils(dynamic raw);
|
||||||
|
|
||||||
|
|
@ -290,6 +313,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseDeserializer deserializer,
|
SseDeserializer deserializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPreKeyBundle sse_decode_box_autoadd_frb_pre_key_bundle(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 sse_decode_box_autoadd_i_64(SseDeserializer deserializer);
|
PlatformInt64 sse_decode_box_autoadd_i_64(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
|
@ -301,6 +329,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseDeserializer deserializer,
|
SseDeserializer deserializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int sse_decode_box_autoadd_u_32(SseDeserializer deserializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
UserDiscoveryStoreFlutter sse_decode_box_autoadd_user_discovery_store_flutter(
|
UserDiscoveryStoreFlutter sse_decode_box_autoadd_user_discovery_store_flutter(
|
||||||
SseDeserializer deserializer,
|
SseDeserializer deserializer,
|
||||||
|
|
@ -316,6 +347,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseDeserializer deserializer,
|
SseDeserializer deserializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPqcPreKey sse_decode_frb_pqc_pre_key(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FrbPreKeyBundle sse_decode_frb_pre_key_bundle(SseDeserializer deserializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer);
|
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
|
@ -325,6 +362,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64 sse_decode_isize(SseDeserializer deserializer);
|
PlatformInt64 sse_decode_isize(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
List<FrbPqcPreKey> sse_decode_list_frb_pqc_pre_key(
|
||||||
|
SseDeserializer deserializer,
|
||||||
|
);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
List<Uint8List> sse_decode_list_list_prim_u_8_strict(
|
List<Uint8List> sse_decode_list_list_prim_u_8_strict(
|
||||||
SseDeserializer deserializer,
|
SseDeserializer deserializer,
|
||||||
|
|
@ -358,6 +400,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
PlatformInt64? sse_decode_opt_box_autoadd_i_64(SseDeserializer deserializer);
|
PlatformInt64? sse_decode_opt_box_autoadd_i_64(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
int? sse_decode_opt_box_autoadd_u_32(SseDeserializer deserializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
List<Uint8List>? sse_decode_opt_list_list_prim_u_8_strict(
|
List<Uint8List>? sse_decode_opt_list_list_prim_u_8_strict(
|
||||||
SseDeserializer deserializer,
|
SseDeserializer deserializer,
|
||||||
|
|
@ -402,6 +447,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
RustKeyManager sse_decode_rust_key_manager(SseDeserializer deserializer);
|
RustKeyManager sse_decode_rust_key_manager(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
RustSignal sse_decode_rust_signal(SseDeserializer deserializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
RustUtils sse_decode_rust_utils(SseDeserializer deserializer);
|
RustUtils sse_decode_rust_utils(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
|
@ -563,6 +611,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseSerializer serializer,
|
SseSerializer serializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_box_autoadd_frb_pre_key_bundle(
|
||||||
|
FrbPreKeyBundle self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_box_autoadd_i_64(
|
void sse_encode_box_autoadd_i_64(
|
||||||
PlatformInt64 self,
|
PlatformInt64 self,
|
||||||
|
|
@ -581,6 +635,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseSerializer serializer,
|
SseSerializer serializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_box_autoadd_user_discovery_store_flutter(
|
void sse_encode_box_autoadd_user_discovery_store_flutter(
|
||||||
UserDiscoveryStoreFlutter self,
|
UserDiscoveryStoreFlutter self,
|
||||||
|
|
@ -599,6 +656,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseSerializer serializer,
|
SseSerializer serializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_frb_pqc_pre_key(FrbPqcPreKey self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_frb_pre_key_bundle(
|
||||||
|
FrbPreKeyBundle self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer);
|
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer);
|
||||||
|
|
||||||
|
|
@ -608,6 +674,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_isize(PlatformInt64 self, SseSerializer serializer);
|
void sse_encode_isize(PlatformInt64 self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_list_frb_pqc_pre_key(
|
||||||
|
List<FrbPqcPreKey> self,
|
||||||
|
SseSerializer serializer,
|
||||||
|
);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_list_list_prim_u_8_strict(
|
void sse_encode_list_list_prim_u_8_strict(
|
||||||
List<Uint8List> self,
|
List<Uint8List> self,
|
||||||
|
|
@ -650,6 +722,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseSerializer serializer,
|
SseSerializer serializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_opt_box_autoadd_u_32(int? self, SseSerializer serializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_opt_list_list_prim_u_8_strict(
|
void sse_encode_opt_list_list_prim_u_8_strict(
|
||||||
List<Uint8List>? self,
|
List<Uint8List>? self,
|
||||||
|
|
@ -710,6 +785,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||||
SseSerializer serializer,
|
SseSerializer serializer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_rust_signal(RustSignal self, SseSerializer serializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_rust_utils(RustUtils self, SseSerializer serializer);
|
void sse_encode_rust_utils(RustUtils self, SseSerializer serializer);
|
||||||
|
|
||||||
|
|
|
||||||
101
lib/core/signal/engine.dart
Normal file
101
lib/core/signal/engine.dart
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
// This file is automatically generated, so please do not edit it.
|
||||||
|
// @generated by `flutter_rust_bridge`@ 2.12.0.
|
||||||
|
|
||||||
|
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||||
|
|
||||||
|
import '../frb_generated.dart';
|
||||||
|
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||||
|
|
||||||
|
class FrbPqcPreKey {
|
||||||
|
final int eccPreKeyId;
|
||||||
|
final Uint8List eccPreKey;
|
||||||
|
final int kyberPreKeyId;
|
||||||
|
final Uint8List kyberPreKey;
|
||||||
|
final Uint8List kyberPreKeySignature;
|
||||||
|
|
||||||
|
const FrbPqcPreKey({
|
||||||
|
required this.eccPreKeyId,
|
||||||
|
required this.eccPreKey,
|
||||||
|
required this.kyberPreKeyId,
|
||||||
|
required this.kyberPreKey,
|
||||||
|
required this.kyberPreKeySignature,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
eccPreKeyId.hashCode ^
|
||||||
|
eccPreKey.hashCode ^
|
||||||
|
kyberPreKeyId.hashCode ^
|
||||||
|
kyberPreKey.hashCode ^
|
||||||
|
kyberPreKeySignature.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is FrbPqcPreKey &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
eccPreKeyId == other.eccPreKeyId &&
|
||||||
|
eccPreKey == other.eccPreKey &&
|
||||||
|
kyberPreKeyId == other.kyberPreKeyId &&
|
||||||
|
kyberPreKey == other.kyberPreKey &&
|
||||||
|
kyberPreKeySignature == other.kyberPreKeySignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
class FrbPreKeyBundle {
|
||||||
|
final int registrationId;
|
||||||
|
final int deviceId;
|
||||||
|
final int? preKeyId;
|
||||||
|
final Uint8List? preKeyPublic;
|
||||||
|
final int signedPreKeyId;
|
||||||
|
final Uint8List signedPreKeyPublic;
|
||||||
|
final Uint8List signedPreKeySignature;
|
||||||
|
final int kyberPreKeyId;
|
||||||
|
final Uint8List kyberPreKeyPublic;
|
||||||
|
final Uint8List kyberPreKeySignature;
|
||||||
|
final Uint8List identityKey;
|
||||||
|
|
||||||
|
const FrbPreKeyBundle({
|
||||||
|
required this.registrationId,
|
||||||
|
required this.deviceId,
|
||||||
|
this.preKeyId,
|
||||||
|
this.preKeyPublic,
|
||||||
|
required this.signedPreKeyId,
|
||||||
|
required this.signedPreKeyPublic,
|
||||||
|
required this.signedPreKeySignature,
|
||||||
|
required this.kyberPreKeyId,
|
||||||
|
required this.kyberPreKeyPublic,
|
||||||
|
required this.kyberPreKeySignature,
|
||||||
|
required this.identityKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
registrationId.hashCode ^
|
||||||
|
deviceId.hashCode ^
|
||||||
|
preKeyId.hashCode ^
|
||||||
|
preKeyPublic.hashCode ^
|
||||||
|
signedPreKeyId.hashCode ^
|
||||||
|
signedPreKeyPublic.hashCode ^
|
||||||
|
signedPreKeySignature.hashCode ^
|
||||||
|
kyberPreKeyId.hashCode ^
|
||||||
|
kyberPreKeyPublic.hashCode ^
|
||||||
|
kyberPreKeySignature.hashCode ^
|
||||||
|
identityKey.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is FrbPreKeyBundle &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
registrationId == other.registrationId &&
|
||||||
|
deviceId == other.deviceId &&
|
||||||
|
preKeyId == other.preKeyId &&
|
||||||
|
preKeyPublic == other.preKeyPublic &&
|
||||||
|
signedPreKeyId == other.signedPreKeyId &&
|
||||||
|
signedPreKeyPublic == other.signedPreKeyPublic &&
|
||||||
|
signedPreKeySignature == other.signedPreKeySignature &&
|
||||||
|
kyberPreKeyId == other.kyberPreKeyId &&
|
||||||
|
kyberPreKeyPublic == other.kyberPreKeyPublic &&
|
||||||
|
kyberPreKeySignature == other.kyberPreKeySignature &&
|
||||||
|
identityKey == other.identityKey;
|
||||||
|
}
|
||||||
|
|
@ -175,13 +175,15 @@ Future<void> postStartupTasks() async {
|
||||||
// 2. Service initializations
|
// 2. Service initializations
|
||||||
unawaited(finishStartedPreprocessing());
|
unawaited(finishStartedPreprocessing());
|
||||||
unawaited(createPushAvatars());
|
unawaited(createPushAvatars());
|
||||||
unawaited(newsService.init().then((_) {
|
unawaited(
|
||||||
|
newsService.init().then((_) {
|
||||||
final lastDownload = newsService.lastDownloadedAt;
|
final lastDownload = newsService.lastDownloadedAt;
|
||||||
if (lastDownload == null ||
|
if (lastDownload == null ||
|
||||||
DateTime.now().difference(lastDownload) >= const Duration(days: 7)) {
|
DateTime.now().difference(lastDownload) >= const Duration(days: 7)) {
|
||||||
newsService.fetchFeed();
|
newsService.fetchFeed();
|
||||||
}
|
}
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
unawaited(UserDiscoveryService.verifyInitializationOnStartup());
|
unawaited(UserDiscoveryService.verifyInitializationOnStartup());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -340,6 +340,24 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
|
||||||
.write(GroupsCompanion(lastMessageExchange: Value(clampedLastMessage)));
|
.write(GroupsCompanion(lastMessageExchange: Value(clampedLastMessage)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> increaseMemberLastMessage(
|
||||||
|
String groupId,
|
||||||
|
int contactId,
|
||||||
|
DateTime newLastMessage,
|
||||||
|
) async {
|
||||||
|
final now = clock.now();
|
||||||
|
final clampedLastMessage =
|
||||||
|
newLastMessage.isAfter(now) ? now : newLastMessage;
|
||||||
|
await (update(groupMembers)..where(
|
||||||
|
(t) =>
|
||||||
|
t.groupId.equals(groupId) &
|
||||||
|
t.contactId.equals(contactId) &
|
||||||
|
(t.lastMessage.isNull() |
|
||||||
|
t.lastMessage.isSmallerThanValue(clampedLastMessage)),
|
||||||
|
))
|
||||||
|
.write(GroupMembersCompanion(lastMessage: Value(clampedLastMessage)));
|
||||||
|
}
|
||||||
|
|
||||||
Stream<List<Group>> watchNonDirectGroupsForMember(int contactId) {
|
Stream<List<Group>> watchNonDirectGroupsForMember(int contactId) {
|
||||||
final query =
|
final query =
|
||||||
select(groups).join([
|
select(groups).join([
|
||||||
|
|
|
||||||
91
lib/src/database/daos/labels.dao.dart
Normal file
91
lib/src/database/daos/labels.dao.dart
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:twonly/src/database/tables/labels.table.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
|
||||||
|
part 'labels.dao.g.dart';
|
||||||
|
|
||||||
|
@DriftAccessor(
|
||||||
|
tables: [
|
||||||
|
Labels,
|
||||||
|
ContactLabels,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
class LabelsDao extends DatabaseAccessor<TwonlyDB> with _$LabelsDaoMixin {
|
||||||
|
LabelsDao(super.db);
|
||||||
|
|
||||||
|
Stream<List<Label>> watchAllLabels() {
|
||||||
|
return (select(labels)..orderBy([(t) => OrderingTerm(expression: t.name)])).watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Label>> getAllLabels() {
|
||||||
|
return (select(labels)..orderBy([(t) => OrderingTerm(expression: t.name)])).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<List<Label>> watchContactLabels(int contactId) {
|
||||||
|
final query = select(contactLabels).join([
|
||||||
|
innerJoin(labels, labels.id.equalsExp(contactLabels.labelId)),
|
||||||
|
])..where(contactLabels.contactId.equals(contactId));
|
||||||
|
|
||||||
|
return query.watch().map(
|
||||||
|
(rows) => rows.map((row) => row.readTable(labels)).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Label>> getContactLabels(int contactId) {
|
||||||
|
final query = select(contactLabels).join([
|
||||||
|
innerJoin(labels, labels.id.equalsExp(contactLabels.labelId)),
|
||||||
|
])..where(contactLabels.contactId.equals(contactId));
|
||||||
|
|
||||||
|
return query.get().then(
|
||||||
|
(rows) => rows.map((row) => row.readTable(labels)).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setContactLabels(int contactId, List<int> labelIds) async {
|
||||||
|
final sanitizedLabelIds = labelIds.take(3).toList();
|
||||||
|
await transaction(() async {
|
||||||
|
await (delete(contactLabels)..where((t) => t.contactId.equals(contactId))).go();
|
||||||
|
if (sanitizedLabelIds.isNotEmpty) {
|
||||||
|
await batch((b) {
|
||||||
|
b.insertAll(
|
||||||
|
contactLabels,
|
||||||
|
sanitizedLabelIds.map(
|
||||||
|
(lId) => ContactLabelsCompanion.insert(
|
||||||
|
contactId: contactId,
|
||||||
|
labelId: lId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> createLabel(String name, int textColor, int backgroundColor) {
|
||||||
|
final sanitizedName = name.length > 8 ? name.substring(0, 8) : name;
|
||||||
|
return into(labels).insert(
|
||||||
|
LabelsCompanion.insert(
|
||||||
|
name: sanitizedName,
|
||||||
|
textColor: textColor,
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> updateLabel(int id, String name, int textColor, int backgroundColor) {
|
||||||
|
final sanitizedName = name.length > 8 ? name.substring(0, 8) : name;
|
||||||
|
return (update(labels)..where((t) => t.id.equals(id)))
|
||||||
|
.write(
|
||||||
|
LabelsCompanion(
|
||||||
|
name: Value(sanitizedName),
|
||||||
|
textColor: Value(textColor),
|
||||||
|
backgroundColor: Value(backgroundColor),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.then((rows) => rows > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> deleteLabel(int id) {
|
||||||
|
return (delete(labels)..where((t) => t.id.equals(id))).go();
|
||||||
|
}
|
||||||
|
}
|
||||||
22
lib/src/database/daos/labels.dao.g.dart
Normal file
22
lib/src/database/daos/labels.dao.g.dart
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'labels.dao.dart';
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
mixin _$LabelsDaoMixin on DatabaseAccessor<TwonlyDB> {
|
||||||
|
$LabelsTable get labels => attachedDatabase.labels;
|
||||||
|
$ContactsTable get contacts => attachedDatabase.contacts;
|
||||||
|
$ContactLabelsTable get contactLabels => attachedDatabase.contactLabels;
|
||||||
|
LabelsDaoManager get managers => LabelsDaoManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LabelsDaoManager {
|
||||||
|
final _$LabelsDaoMixin _db;
|
||||||
|
LabelsDaoManager(this._db);
|
||||||
|
$$LabelsTableTableManager get labels =>
|
||||||
|
$$LabelsTableTableManager(_db.attachedDatabase, _db.labels);
|
||||||
|
$$ContactsTableTableManager get contacts =>
|
||||||
|
$$ContactsTableTableManager(_db.attachedDatabase, _db.contacts);
|
||||||
|
$$ContactLabelsTableTableManager get contactLabels =>
|
||||||
|
$$ContactLabelsTableTableManager(_db.attachedDatabase, _db.contactLabels);
|
||||||
|
}
|
||||||
|
|
@ -489,22 +489,28 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
|
|
||||||
await into(messages).insertOnConflictUpdate(insertMessage);
|
await into(messages).insertOnConflictUpdate(insertMessage);
|
||||||
|
|
||||||
|
final msgTime = insertMessage.createdAt.present
|
||||||
|
? insertMessage.createdAt.value
|
||||||
|
: clock.now();
|
||||||
|
|
||||||
await twonlyDB.groupsDao.updateGroup(
|
await twonlyDB.groupsDao.updateGroup(
|
||||||
message.groupId.value,
|
message.groupId.value,
|
||||||
GroupsCompanion(
|
const GroupsCompanion(
|
||||||
lastMessageExchange: Value(clock.now()),
|
archived: Value(false),
|
||||||
archived: const Value(false),
|
deletedContent: Value(false),
|
||||||
deletedContent: const Value(false),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (message.senderId.present) {
|
await twonlyDB.groupsDao.increaseLastMessageExchange(
|
||||||
await twonlyDB.groupsDao.updateMember(
|
message.groupId.value,
|
||||||
|
msgTime,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (message.senderId.present && message.senderId.value != null) {
|
||||||
|
await twonlyDB.groupsDao.increaseMemberLastMessage(
|
||||||
message.groupId.value,
|
message.groupId.value,
|
||||||
message.senderId.value!,
|
message.senderId.value!,
|
||||||
GroupMembersCompanion(
|
msgTime,
|
||||||
lastMessage: Value(clock.now()),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
3322
lib/src/database/schemas/twonly_db/drift_schema_v24.json
Normal file
3322
lib/src/database/schemas/twonly_db/drift_schema_v24.json
Normal file
File diff suppressed because it is too large
Load diff
3091
lib/src/database/schemas/twonly_db/drift_schema_v25.json
Normal file
3091
lib/src/database/schemas/twonly_db/drift_schema_v25.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -63,6 +63,7 @@ class SignalSignedPreKeyStore extends SignedPreKeyStore {
|
||||||
.insert(companion, mode: InsertMode.insertOrReplace);
|
.insert(companion, mode: InsertMode.insertOrReplace);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.error('$e');
|
Log.error('$e');
|
||||||
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
||||||
|
enum SignalVersion { v1, v2 }
|
||||||
|
|
||||||
@DataClassName('Contact')
|
@DataClassName('Contact')
|
||||||
class Contacts extends Table {
|
class Contacts extends Table {
|
||||||
IntColumn get userId => integer()();
|
IntColumn get userId => integer()();
|
||||||
|
|
@ -23,6 +26,9 @@ class Contacts extends Table {
|
||||||
|
|
||||||
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||||
|
|
||||||
|
TextColumn get signalVersion =>
|
||||||
|
textEnum<SignalVersion>().withDefault(const Constant('v1'))();
|
||||||
|
|
||||||
// User Discovery
|
// User Discovery
|
||||||
BlobColumn get userDiscoveryVersion => blob().nullable()();
|
BlobColumn get userDiscoveryVersion => blob().nullable()();
|
||||||
BoolColumn get userDiscoveryExcluded =>
|
BoolColumn get userDiscoveryExcluded =>
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ class Groups extends Table {
|
||||||
boolean().withDefault(const Constant(false))();
|
boolean().withDefault(const Constant(false))();
|
||||||
|
|
||||||
IntColumn get deleteMessagesAfterMilliseconds => integer().withDefault(
|
IntColumn get deleteMessagesAfterMilliseconds => integer().withDefault(
|
||||||
const Constant(defaultDeleteMessagesAfterMilliseconds),
|
const Constant(1000 * 60 * 60 * 24),
|
||||||
)();
|
)();
|
||||||
|
|
||||||
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||||
|
|
|
||||||
28
lib/src/database/tables/labels.table.dart
Normal file
28
lib/src/database/tables/labels.table.dart
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:twonly/src/database/tables/contacts.table.dart';
|
||||||
|
|
||||||
|
@DataClassName('Label')
|
||||||
|
class Labels extends Table {
|
||||||
|
IntColumn get id => integer().autoIncrement()();
|
||||||
|
TextColumn get name => text()();
|
||||||
|
IntColumn get textColor => integer()();
|
||||||
|
IntColumn get backgroundColor => integer()();
|
||||||
|
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DataClassName('ContactLabel')
|
||||||
|
class ContactLabels extends Table {
|
||||||
|
IntColumn get contactId => integer().references(
|
||||||
|
Contacts,
|
||||||
|
#userId,
|
||||||
|
onDelete: KeyAction.cascade,
|
||||||
|
)();
|
||||||
|
IntColumn get labelId => integer().references(
|
||||||
|
Labels,
|
||||||
|
#id,
|
||||||
|
onDelete: KeyAction.cascade,
|
||||||
|
)();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column> get primaryKey => {contactId, labelId};
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/daos/groups.dao.dart';
|
import 'package:twonly/src/database/daos/groups.dao.dart';
|
||||||
import 'package:twonly/src/database/daos/key_verification.dao.dart';
|
import 'package:twonly/src/database/daos/key_verification.dao.dart';
|
||||||
|
import 'package:twonly/src/database/daos/labels.dao.dart';
|
||||||
import 'package:twonly/src/database/daos/mediafiles.dao.dart';
|
import 'package:twonly/src/database/daos/mediafiles.dao.dart';
|
||||||
import 'package:twonly/src/database/daos/messages.dao.dart';
|
import 'package:twonly/src/database/daos/messages.dao.dart';
|
||||||
import 'package:twonly/src/database/daos/reactions.dao.dart';
|
import 'package:twonly/src/database/daos/reactions.dao.dart';
|
||||||
|
|
@ -15,6 +16,7 @@ import 'package:twonly/src/database/daos/user_discovery.dao.dart';
|
||||||
import 'package:twonly/src/database/drift_logging_interceptor.dart';
|
import 'package:twonly/src/database/drift_logging_interceptor.dart';
|
||||||
import 'package:twonly/src/database/tables/contacts.table.dart';
|
import 'package:twonly/src/database/tables/contacts.table.dart';
|
||||||
import 'package:twonly/src/database/tables/groups.table.dart';
|
import 'package:twonly/src/database/tables/groups.table.dart';
|
||||||
|
import 'package:twonly/src/database/tables/labels.table.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/database/tables/messages.table.dart';
|
import 'package:twonly/src/database/tables/messages.table.dart';
|
||||||
import 'package:twonly/src/database/tables/reactions.table.dart';
|
import 'package:twonly/src/database/tables/reactions.table.dart';
|
||||||
|
|
@ -59,6 +61,8 @@ part 'twonly.db.g.dart';
|
||||||
UserDiscoveryShares,
|
UserDiscoveryShares,
|
||||||
Shortcuts,
|
Shortcuts,
|
||||||
ShortcutMembers,
|
ShortcutMembers,
|
||||||
|
Labels,
|
||||||
|
ContactLabels,
|
||||||
],
|
],
|
||||||
daos: [
|
daos: [
|
||||||
MessagesDao,
|
MessagesDao,
|
||||||
|
|
@ -70,6 +74,7 @@ part 'twonly.db.g.dart';
|
||||||
UserDiscoveryDao,
|
UserDiscoveryDao,
|
||||||
KeyVerificationDao,
|
KeyVerificationDao,
|
||||||
ShortcutsDao,
|
ShortcutsDao,
|
||||||
|
LabelsDao,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
class TwonlyDB extends _$TwonlyDB {
|
class TwonlyDB extends _$TwonlyDB {
|
||||||
|
|
@ -82,7 +87,7 @@ class TwonlyDB extends _$TwonlyDB {
|
||||||
TwonlyDB.forTesting(DatabaseConnection super.connection);
|
TwonlyDB.forTesting(DatabaseConnection super.connection);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 23;
|
int get schemaVersion => 25;
|
||||||
|
|
||||||
static QueryExecutor _openConnection() {
|
static QueryExecutor _openConnection() {
|
||||||
final connection = driftDatabase(
|
final connection = driftDatabase(
|
||||||
|
|
@ -285,6 +290,13 @@ class TwonlyDB extends _$TwonlyDB {
|
||||||
schema.mediaFiles.blurhash,
|
schema.mediaFiles.blurhash,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
from23To24: (m, schema) async {
|
||||||
|
await m.createTable(schema.labels);
|
||||||
|
await m.createTable(schema.contactLabels);
|
||||||
|
},
|
||||||
|
from24To25: (m, schema) async {
|
||||||
|
await m.addColumn(schema.contacts, schema.contacts.signalVersion);
|
||||||
|
},
|
||||||
)(m, from, to);
|
)(m, from, to);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1424,6 +1424,12 @@ abstract class AppLocalizations {
|
||||||
/// **'Delete for me'**
|
/// **'Delete for me'**
|
||||||
String get deleteOkBtnForMe;
|
String get deleteOkBtnForMe;
|
||||||
|
|
||||||
|
/// No description provided for @deleteOnlyForMe.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Delete only for me'**
|
||||||
|
String get deleteOnlyForMe;
|
||||||
|
|
||||||
/// No description provided for @deleteImageTitle.
|
/// No description provided for @deleteImageTitle.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
@ -1541,7 +1547,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @backupArchiveHeader.
|
/// No description provided for @backupArchiveHeader.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Contacts, Settings and Messages'**
|
/// **'Contacts & Messages'**
|
||||||
String get backupArchiveHeader;
|
String get backupArchiveHeader;
|
||||||
|
|
||||||
/// No description provided for @backupLastBackupDate.
|
/// No description provided for @backupLastBackupDate.
|
||||||
|
|
@ -1595,7 +1601,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @backupSelectStrongPassword.
|
/// No description provided for @backupSelectStrongPassword.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Choose a secure password. This is required if you want to restore your twonly Backup.'**
|
/// **'Choose a secure password. This is required if you want to restore your backup.'**
|
||||||
String get backupSelectStrongPassword;
|
String get backupSelectStrongPassword;
|
||||||
|
|
||||||
/// No description provided for @password.
|
/// No description provided for @password.
|
||||||
|
|
@ -1652,6 +1658,36 @@ abstract class AppLocalizations {
|
||||||
/// **'You can only change your password after you have authenticated!'**
|
/// **'You can only change your password after you have authenticated!'**
|
||||||
String get backupChangePasswordAuthFailed;
|
String get backupChangePasswordAuthFailed;
|
||||||
|
|
||||||
|
/// No description provided for @backupFreeSpaceWithCloud.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Free Space with Cloud Backup'**
|
||||||
|
String get backupFreeSpaceWithCloud;
|
||||||
|
|
||||||
|
/// No description provided for @backupMemoriesNotEnabled.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Not enabled'**
|
||||||
|
String get backupMemoriesNotEnabled;
|
||||||
|
|
||||||
|
/// No description provided for @backupMemoriesUpgradeRequired.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Upgrade required'**
|
||||||
|
String get backupMemoriesUpgradeRequired;
|
||||||
|
|
||||||
|
/// No description provided for @todayAt.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Today at {time}'**
|
||||||
|
String todayAt(Object time);
|
||||||
|
|
||||||
|
/// No description provided for @yesterdayAt.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Yesterday at {time}'**
|
||||||
|
String yesterdayAt(Object time);
|
||||||
|
|
||||||
/// No description provided for @twonlySafeRecoverTitle.
|
/// No description provided for @twonlySafeRecoverTitle.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
@ -3404,6 +3440,12 @@ abstract class AppLocalizations {
|
||||||
/// **'Zero ads. Total privacy.'**
|
/// **'Zero ads. Total privacy.'**
|
||||||
String get subscriptionPledgeSubtitle;
|
String get subscriptionPledgeSubtitle;
|
||||||
|
|
||||||
|
/// No description provided for @subscriptionManage.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Manage subscription'**
|
||||||
|
String get subscriptionManage;
|
||||||
|
|
||||||
/// No description provided for @dragToZoom.
|
/// No description provided for @dragToZoom.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
@ -3671,7 +3713,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @passwordlessRecovery.
|
/// No description provided for @passwordlessRecovery.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Passwordless Recovery'**
|
/// **'Password Recovery'**
|
||||||
String get passwordlessRecovery;
|
String get passwordlessRecovery;
|
||||||
|
|
||||||
/// No description provided for @passwordlessRecoveryNotConfigured.
|
/// No description provided for @passwordlessRecoveryNotConfigured.
|
||||||
|
|
@ -3917,7 +3959,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @passwordlessRecoveryTrustedFriends.
|
/// No description provided for @passwordlessRecoveryTrustedFriends.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Trusted Friends'**
|
/// **'Account recovery'**
|
||||||
String get passwordlessRecoveryTrustedFriends;
|
String get passwordlessRecoveryTrustedFriends;
|
||||||
|
|
||||||
/// No description provided for @passwordlessRecoveryDoneBtn.
|
/// No description provided for @passwordlessRecoveryDoneBtn.
|
||||||
|
|
@ -4097,7 +4139,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @missingRecoveryContactsCardTitle.
|
/// No description provided for @missingRecoveryContactsCardTitle.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Recovery Contacts'**
|
/// **'Account Recovery'**
|
||||||
String get missingRecoveryContactsCardTitle;
|
String get missingRecoveryContactsCardTitle;
|
||||||
|
|
||||||
/// No description provided for @missingRecoveryContactsCardDesc.
|
/// No description provided for @missingRecoveryContactsCardDesc.
|
||||||
|
|
@ -4193,7 +4235,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @settingsStorageContents.
|
/// No description provided for @settingsStorageContents.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Storage contents'**
|
/// **'Free up space'**
|
||||||
String get settingsStorageContents;
|
String get settingsStorageContents;
|
||||||
|
|
||||||
/// No description provided for @settingsStorageSortStorage.
|
/// No description provided for @settingsStorageSortStorage.
|
||||||
|
|
@ -4273,6 +4315,126 @@ abstract class AppLocalizations {
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'All memories are up to date.'**
|
/// **'All memories are up to date.'**
|
||||||
String get settingsStorageSyncUpToDate;
|
String get settingsStorageSyncUpToDate;
|
||||||
|
|
||||||
|
/// No description provided for @restoreLostFlames.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Restore your {count} lost flames'**
|
||||||
|
String restoreLostFlames(int count);
|
||||||
|
|
||||||
|
/// No description provided for @settingsShowRestoreFlameTitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Show flame restore warning'**
|
||||||
|
String get settingsShowRestoreFlameTitle;
|
||||||
|
|
||||||
|
/// No description provided for @contactLabelsTitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Contact Labels'**
|
||||||
|
String get contactLabelsTitle;
|
||||||
|
|
||||||
|
/// No description provided for @contactLabelsSubtitleEmpty.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'No labels selected'**
|
||||||
|
String get contactLabelsSubtitleEmpty;
|
||||||
|
|
||||||
|
/// No description provided for @contactLabelsMaxLimit.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Maximum 3 labels per contact'**
|
||||||
|
String get contactLabelsMaxLimit;
|
||||||
|
|
||||||
|
/// No description provided for @createLabel.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Create new label'**
|
||||||
|
String get createLabel;
|
||||||
|
|
||||||
|
/// No description provided for @editLabel.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Edit label'**
|
||||||
|
String get editLabel;
|
||||||
|
|
||||||
|
/// No description provided for @deleteLabel.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Delete label'**
|
||||||
|
String get deleteLabel;
|
||||||
|
|
||||||
|
/// No description provided for @deleteLabelConfirmation.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Are you sure you want to delete this label? It will be removed from all contacts.'**
|
||||||
|
String get deleteLabelConfirmation;
|
||||||
|
|
||||||
|
/// No description provided for @labelNameHint.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Label name'**
|
||||||
|
String get labelNameHint;
|
||||||
|
|
||||||
|
/// No description provided for @labelTextColor.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Text color'**
|
||||||
|
String get labelTextColor;
|
||||||
|
|
||||||
|
/// No description provided for @labelBackgroundColor.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Background color'**
|
||||||
|
String get labelBackgroundColor;
|
||||||
|
|
||||||
|
/// No description provided for @customColor.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Custom Color'**
|
||||||
|
String get customColor;
|
||||||
|
|
||||||
|
/// No description provided for @hue.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Hue'**
|
||||||
|
String get hue;
|
||||||
|
|
||||||
|
/// No description provided for @saturation.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Saturation'**
|
||||||
|
String get saturation;
|
||||||
|
|
||||||
|
/// No description provided for @brightness.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Brightness'**
|
||||||
|
String get brightness;
|
||||||
|
|
||||||
|
/// No description provided for @settingsAppearancePrimaryColor.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Primary Color'**
|
||||||
|
String get settingsAppearancePrimaryColor;
|
||||||
|
|
||||||
|
/// No description provided for @themeSystemDefault.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'System default'**
|
||||||
|
String get themeSystemDefault;
|
||||||
|
|
||||||
|
/// No description provided for @themeLight.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Light'**
|
||||||
|
String get themeLight;
|
||||||
|
|
||||||
|
/// No description provided for @themeDark.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Dark'**
|
||||||
|
String get themeDark;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppLocalizationsDelegate
|
class _AppLocalizationsDelegate
|
||||||
|
|
|
||||||
|
|
@ -741,6 +741,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get deleteOkBtnForMe => 'Für mich löschen';
|
String get deleteOkBtnForMe => 'Für mich löschen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteOnlyForMe => 'Nur für mich löschen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get deleteImageTitle => 'Bist du dir sicher?';
|
String get deleteImageTitle => 'Bist du dir sicher?';
|
||||||
|
|
||||||
|
|
@ -817,7 +820,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String get backupIdentityHeader => 'Identität';
|
String get backupIdentityHeader => 'Identität';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get backupArchiveHeader => 'Kontakte, Einstellungen und Nachrichten';
|
String get backupArchiveHeader => 'Kontakte & Nachrichten';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get backupLastBackupDate => 'Letztes Backup';
|
String get backupLastBackupDate => 'Letztes Backup';
|
||||||
|
|
@ -847,7 +850,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get backupSelectStrongPassword =>
|
String get backupSelectStrongPassword =>
|
||||||
'Wähle ein sicheres Passwort. Dies ist erforderlich, wenn du dein twonly Backup wiederherstellen möchtest.';
|
'Wähle ein sicheres Passwort. Dies ist erforderlich, wenn du dein Backup wiederherstellen möchtest.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get password => 'Passwort';
|
String get password => 'Passwort';
|
||||||
|
|
@ -878,6 +881,26 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String get backupChangePasswordAuthFailed =>
|
String get backupChangePasswordAuthFailed =>
|
||||||
'Du kannst dein Passwort nur ändern, wenn du dich authentifiziert hast!';
|
'Du kannst dein Passwort nur ändern, wenn du dich authentifiziert hast!';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get backupFreeSpaceWithCloud =>
|
||||||
|
'Speicherplatz mit Cloud-Backup freigeben';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get backupMemoriesNotEnabled => 'Nicht aktiviert';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get backupMemoriesUpgradeRequired => 'Upgrade erforderlich';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String todayAt(Object time) {
|
||||||
|
return 'Heute um $time';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String yesterdayAt(Object time) {
|
||||||
|
return 'Gestern um $time';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get twonlySafeRecoverTitle => 'Backup wiederherstellen';
|
String get twonlySafeRecoverTitle => 'Backup wiederherstellen';
|
||||||
|
|
||||||
|
|
@ -1960,6 +1983,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get subscriptionPledgeSubtitle => 'Keine Werbung. Volle Privatsphäre.';
|
String get subscriptionPledgeSubtitle => 'Keine Werbung. Volle Privatsphäre.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get subscriptionManage => 'Abonnement verwalten';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get dragToZoom => 'Zum Zoomen ziehen';
|
String get dragToZoom => 'Zum Zoomen ziehen';
|
||||||
|
|
||||||
|
|
@ -2118,7 +2144,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String get avatarCustomizeReset => 'Zurücksetzen';
|
String get avatarCustomizeReset => 'Zurücksetzen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get passwordlessRecovery => 'Passwortloses Backup';
|
String get passwordlessRecovery => 'Passwort vergessen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get passwordlessRecoveryNotConfigured => 'Nicht konfiguriert';
|
String get passwordlessRecoveryNotConfigured => 'Nicht konfiguriert';
|
||||||
|
|
@ -2184,7 +2210,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get passwordlessRecoveryEnableSuccess =>
|
String get passwordlessRecoveryEnableSuccess =>
|
||||||
'Passwortloses Backup erfolgreich aktiviert!';
|
'\"Passwort vergessen\" erfolgreich aktiviert!';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get passwordlessRecoveryEnterPin => 'Bitte gib eine PIN ein.';
|
String get passwordlessRecoveryEnterPin => 'Bitte gib eine PIN ein.';
|
||||||
|
|
@ -2194,13 +2220,16 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
'Bitte gib eine E-Mail-Adresse ein.';
|
'Bitte gib eine E-Mail-Adresse ein.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get passwordlessRecoveryEnableBtn => 'Passwortloses Backup aktivieren';
|
String get passwordlessRecoveryEnableBtn =>
|
||||||
|
'\"Passwort vergessen\" aktivieren';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get passwordlessRecoveryRecoverBtn => 'Passwortlos wiederherstellen';
|
String get passwordlessRecoveryRecoverBtn =>
|
||||||
|
'Mit \"Passwort vergessen\" wiederherstellen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get passwordlessRecoveryModifyBtn => 'Passwortloses Backup bearbeiten';
|
String get passwordlessRecoveryModifyBtn =>
|
||||||
|
'\"Passwort vergessen\" bearbeiten';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String passwordlessRecoveryStatusEnabled(num count) {
|
String passwordlessRecoveryStatusEnabled(num count) {
|
||||||
|
|
@ -2261,7 +2290,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String get passwordlessRecoverySelectFriends => 'Freunde auswählen';
|
String get passwordlessRecoverySelectFriends => 'Freunde auswählen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get passwordlessRecoveryTrustedFriends => 'Vertrauenswürdige Freunde';
|
String get passwordlessRecoveryTrustedFriends => 'Kontowiederherstellung';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String passwordlessRecoveryDoneBtn(num count) {
|
String passwordlessRecoveryDoneBtn(num count) {
|
||||||
|
|
@ -2375,7 +2404,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String get recoverPasswordlessRecoverNowBtn => 'Jetzt wiederherstellen';
|
String get recoverPasswordlessRecoverNowBtn => 'Jetzt wiederherstellen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get missingRecoveryContactsCardTitle => 'Vertrauenswürdige Kontakte';
|
String get missingRecoveryContactsCardTitle => 'Kontowiederherstellung';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get missingRecoveryContactsCardDesc =>
|
String get missingRecoveryContactsCardDesc =>
|
||||||
|
|
@ -2426,7 +2455,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String get settingsHelpNews => 'Neuigkeiten';
|
String get settingsHelpNews => 'Neuigkeiten';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsStorageContents => 'Speicherinhalte';
|
String get settingsStorageContents => 'Speicherplatz freigeben';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsStorageSortStorage => 'Belegter Speicher';
|
String get settingsStorageSortStorage => 'Belegter Speicher';
|
||||||
|
|
@ -2475,4 +2504,68 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get settingsStorageSyncUpToDate =>
|
String get settingsStorageSyncUpToDate =>
|
||||||
'Alle Memories sind auf dem neuesten Stand.';
|
'Alle Memories sind auf dem neuesten Stand.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String restoreLostFlames(int count) {
|
||||||
|
return 'Stelle deine $count verlorenen Flammen wieder her';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settingsShowRestoreFlameTitle =>
|
||||||
|
'Hinweis zur Flammen-Wiederherstellung anzeigen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contactLabelsTitle => 'Kontaktlabels';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contactLabelsSubtitleEmpty => 'Keine Labels ausgewählt';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contactLabelsMaxLimit => 'Maximal 3 Labels pro Kontakt';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get createLabel => 'Neues Label erstellen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get editLabel => 'Label bearbeiten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteLabel => 'Label löschen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteLabelConfirmation =>
|
||||||
|
'Möchtest du dieses Label wirklich löschen? Es wird von allen Kontakten entfernt.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get labelNameHint => 'Label-Name';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get labelTextColor => 'Textfarbe';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get labelBackgroundColor => 'Hintergrundfarbe';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get customColor => 'Eigene Farbe';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get hue => 'Farbton';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get saturation => 'Sättigung';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get brightness => 'Helligkeit';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settingsAppearancePrimaryColor => 'Hauptfarbe';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get themeSystemDefault => 'Systemstandard';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get themeLight => 'Hell';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get themeDark => 'Dunkel';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -736,6 +736,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get deleteOkBtnForMe => 'Delete for me';
|
String get deleteOkBtnForMe => 'Delete for me';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteOnlyForMe => 'Delete only for me';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get deleteImageTitle => 'Are you sure?';
|
String get deleteImageTitle => 'Are you sure?';
|
||||||
|
|
||||||
|
|
@ -812,7 +815,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
String get backupIdentityHeader => 'Identity';
|
String get backupIdentityHeader => 'Identity';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get backupArchiveHeader => 'Contacts, Settings and Messages';
|
String get backupArchiveHeader => 'Contacts & Messages';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get backupLastBackupDate => 'Last backup';
|
String get backupLastBackupDate => 'Last backup';
|
||||||
|
|
@ -842,7 +845,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get backupSelectStrongPassword =>
|
String get backupSelectStrongPassword =>
|
||||||
'Choose a secure password. This is required if you want to restore your twonly Backup.';
|
'Choose a secure password. This is required if you want to restore your backup.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get password => 'Password';
|
String get password => 'Password';
|
||||||
|
|
@ -873,6 +876,25 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
String get backupChangePasswordAuthFailed =>
|
String get backupChangePasswordAuthFailed =>
|
||||||
'You can only change your password after you have authenticated!';
|
'You can only change your password after you have authenticated!';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get backupFreeSpaceWithCloud => 'Free Space with Cloud Backup';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get backupMemoriesNotEnabled => 'Not enabled';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get backupMemoriesUpgradeRequired => 'Upgrade required';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String todayAt(Object time) {
|
||||||
|
return 'Today at $time';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String yesterdayAt(Object time) {
|
||||||
|
return 'Yesterday at $time';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get twonlySafeRecoverTitle => 'Restore backup';
|
String get twonlySafeRecoverTitle => 'Restore backup';
|
||||||
|
|
||||||
|
|
@ -1946,6 +1968,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get subscriptionPledgeSubtitle => 'Zero ads. Total privacy.';
|
String get subscriptionPledgeSubtitle => 'Zero ads. Total privacy.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get subscriptionManage => 'Manage subscription';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get dragToZoom => 'Drag to Zoom';
|
String get dragToZoom => 'Drag to Zoom';
|
||||||
|
|
||||||
|
|
@ -2104,7 +2129,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
String get avatarCustomizeReset => 'Reset';
|
String get avatarCustomizeReset => 'Reset';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get passwordlessRecovery => 'Passwordless Recovery';
|
String get passwordlessRecovery => 'Password Recovery';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get passwordlessRecoveryNotConfigured => 'Not configured';
|
String get passwordlessRecoveryNotConfigured => 'Not configured';
|
||||||
|
|
@ -2245,7 +2270,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
String get passwordlessRecoverySelectFriends => 'Select trusted friends';
|
String get passwordlessRecoverySelectFriends => 'Select trusted friends';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get passwordlessRecoveryTrustedFriends => 'Trusted Friends';
|
String get passwordlessRecoveryTrustedFriends => 'Account recovery';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String passwordlessRecoveryDoneBtn(num count) {
|
String passwordlessRecoveryDoneBtn(num count) {
|
||||||
|
|
@ -2354,7 +2379,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
String get recoverPasswordlessRecoverNowBtn => 'Recover now';
|
String get recoverPasswordlessRecoverNowBtn => 'Recover now';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get missingRecoveryContactsCardTitle => 'Recovery Contacts';
|
String get missingRecoveryContactsCardTitle => 'Account Recovery';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get missingRecoveryContactsCardDesc =>
|
String get missingRecoveryContactsCardDesc =>
|
||||||
|
|
@ -2405,7 +2430,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
String get settingsHelpNews => 'News';
|
String get settingsHelpNews => 'News';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsStorageContents => 'Storage contents';
|
String get settingsStorageContents => 'Free up space';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsStorageSortStorage => 'Occupied storage';
|
String get settingsStorageSortStorage => 'Occupied storage';
|
||||||
|
|
@ -2453,4 +2478,67 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsStorageSyncUpToDate => 'All memories are up to date.';
|
String get settingsStorageSyncUpToDate => 'All memories are up to date.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String restoreLostFlames(int count) {
|
||||||
|
return 'Restore your $count lost flames';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settingsShowRestoreFlameTitle => 'Show flame restore warning';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contactLabelsTitle => 'Contact Labels';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contactLabelsSubtitleEmpty => 'No labels selected';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contactLabelsMaxLimit => 'Maximum 3 labels per contact';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get createLabel => 'Create new label';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get editLabel => 'Edit label';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteLabel => 'Delete label';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get deleteLabelConfirmation =>
|
||||||
|
'Are you sure you want to delete this label? It will be removed from all contacts.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get labelNameHint => 'Label name';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get labelTextColor => 'Text color';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get labelBackgroundColor => 'Background color';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get customColor => 'Custom Color';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get hue => 'Hue';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get saturation => 'Saturation';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get brightness => 'Brightness';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get settingsAppearancePrimaryColor => 'Primary Color';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get themeSystemDefault => 'System default';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get themeLight => 'Light';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get themeDark => 'Dark';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 34c835db2de08af962b39fb79ec11341b8847926
|
Subproject commit ce51a6d084db162b4dfb7cbba1c0cda6d8c477d5
|
||||||
|
|
@ -57,6 +57,12 @@ class UserData {
|
||||||
@JsonKey(defaultValue: ThemeMode.system)
|
@JsonKey(defaultValue: ThemeMode.system)
|
||||||
ThemeMode themeMode = ThemeMode.system;
|
ThemeMode themeMode = ThemeMode.system;
|
||||||
|
|
||||||
|
int? primaryColorValue;
|
||||||
|
|
||||||
|
Color get primaryColor => primaryColorValue != null
|
||||||
|
? Color(primaryColorValue!)
|
||||||
|
: const Color(0xFF57CC99);
|
||||||
|
|
||||||
int? defaultShowTime;
|
int? defaultShowTime;
|
||||||
|
|
||||||
@JsonKey(defaultValue: false)
|
@JsonKey(defaultValue: false)
|
||||||
|
|
@ -93,10 +99,15 @@ class UserData {
|
||||||
@JsonKey(defaultValue: true)
|
@JsonKey(defaultValue: true)
|
||||||
bool typingIndicators = true;
|
bool typingIndicators = true;
|
||||||
|
|
||||||
|
@JsonKey(defaultValue: true)
|
||||||
|
bool showRestoreFlame = true;
|
||||||
|
|
||||||
String? myBestFriendGroupId;
|
String? myBestFriendGroupId;
|
||||||
|
|
||||||
DateTime? signalLastSignedPreKeyUpdated;
|
DateTime? signalLastSignedPreKeyUpdated;
|
||||||
|
|
||||||
|
DateTime? signalLastPqcPreKeysUploaded;
|
||||||
|
|
||||||
@JsonKey(defaultValue: false)
|
@JsonKey(defaultValue: false)
|
||||||
bool allowErrorTrackingViaSentry = false;
|
bool allowErrorTrackingViaSentry = false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) =>
|
||||||
..themeMode =
|
..themeMode =
|
||||||
$enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ??
|
$enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ??
|
||||||
ThemeMode.system
|
ThemeMode.system
|
||||||
|
..primaryColorValue = (json['primaryColorValue'] as num?)?.toInt()
|
||||||
..defaultShowTime = (json['defaultShowTime'] as num?)?.toInt()
|
..defaultShowTime = (json['defaultShowTime'] as num?)?.toInt()
|
||||||
..requestedAudioPermission =
|
..requestedAudioPermission =
|
||||||
json['requestedAudioPermission'] as bool? ?? false
|
json['requestedAudioPermission'] as bool? ?? false
|
||||||
|
|
@ -60,11 +61,16 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) =>
|
||||||
..autoStoreAllSendUnlimitedMediaFiles =
|
..autoStoreAllSendUnlimitedMediaFiles =
|
||||||
json['autoStoreAllSendUnlimitedMediaFiles'] as bool? ?? false
|
json['autoStoreAllSendUnlimitedMediaFiles'] as bool? ?? false
|
||||||
..typingIndicators = json['typingIndicators'] as bool? ?? true
|
..typingIndicators = json['typingIndicators'] as bool? ?? true
|
||||||
|
..showRestoreFlame = json['showRestoreFlame'] as bool? ?? true
|
||||||
..myBestFriendGroupId = json['myBestFriendGroupId'] as String?
|
..myBestFriendGroupId = json['myBestFriendGroupId'] as String?
|
||||||
..signalLastSignedPreKeyUpdated =
|
..signalLastSignedPreKeyUpdated =
|
||||||
json['signalLastSignedPreKeyUpdated'] == null
|
json['signalLastSignedPreKeyUpdated'] == null
|
||||||
? null
|
? null
|
||||||
: DateTime.parse(json['signalLastSignedPreKeyUpdated'] as String)
|
: DateTime.parse(json['signalLastSignedPreKeyUpdated'] as String)
|
||||||
|
..signalLastPqcPreKeysUploaded =
|
||||||
|
json['signalLastPqcPreKeysUploaded'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['signalLastPqcPreKeysUploaded'] as String)
|
||||||
..allowErrorTrackingViaSentry =
|
..allowErrorTrackingViaSentry =
|
||||||
json['allowErrorTrackingViaSentry'] as bool? ?? false
|
json['allowErrorTrackingViaSentry'] as bool? ?? false
|
||||||
..screenLockEnabled = json['screenLockEnabled'] as bool? ?? false
|
..screenLockEnabled = json['screenLockEnabled'] as bool? ?? false
|
||||||
|
|
@ -136,6 +142,7 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
|
||||||
'lastPlanBallance': instance.lastPlanBallance,
|
'lastPlanBallance': instance.lastPlanBallance,
|
||||||
'additionalUserInvites': instance.additionalUserInvites,
|
'additionalUserInvites': instance.additionalUserInvites,
|
||||||
'themeMode': _$ThemeModeEnumMap[instance.themeMode]!,
|
'themeMode': _$ThemeModeEnumMap[instance.themeMode]!,
|
||||||
|
'primaryColorValue': instance.primaryColorValue,
|
||||||
'defaultShowTime': instance.defaultShowTime,
|
'defaultShowTime': instance.defaultShowTime,
|
||||||
'requestedAudioPermission': instance.requestedAudioPermission,
|
'requestedAudioPermission': instance.requestedAudioPermission,
|
||||||
'enableDatabaseLogging': instance.enableDatabaseLogging,
|
'enableDatabaseLogging': instance.enableDatabaseLogging,
|
||||||
|
|
@ -151,9 +158,12 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
|
||||||
'autoStoreAllSendUnlimitedMediaFiles':
|
'autoStoreAllSendUnlimitedMediaFiles':
|
||||||
instance.autoStoreAllSendUnlimitedMediaFiles,
|
instance.autoStoreAllSendUnlimitedMediaFiles,
|
||||||
'typingIndicators': instance.typingIndicators,
|
'typingIndicators': instance.typingIndicators,
|
||||||
|
'showRestoreFlame': instance.showRestoreFlame,
|
||||||
'myBestFriendGroupId': instance.myBestFriendGroupId,
|
'myBestFriendGroupId': instance.myBestFriendGroupId,
|
||||||
'signalLastSignedPreKeyUpdated': instance.signalLastSignedPreKeyUpdated
|
'signalLastSignedPreKeyUpdated': instance.signalLastSignedPreKeyUpdated
|
||||||
?.toIso8601String(),
|
?.toIso8601String(),
|
||||||
|
'signalLastPqcPreKeysUploaded': instance.signalLastPqcPreKeysUploaded
|
||||||
|
?.toIso8601String(),
|
||||||
'allowErrorTrackingViaSentry': instance.allowErrorTrackingViaSentry,
|
'allowErrorTrackingViaSentry': instance.allowErrorTrackingViaSentry,
|
||||||
'screenLockEnabled': instance.screenLockEnabled,
|
'screenLockEnabled': instance.screenLockEnabled,
|
||||||
'isCloudBackupEnabled': instance.isCloudBackupEnabled,
|
'isCloudBackupEnabled': instance.isCloudBackupEnabled,
|
||||||
|
|
|
||||||
|
|
@ -2059,6 +2059,250 @@ class ApplicationData_UpdateSignedPreKey extends $pb.GeneratedMessage {
|
||||||
void clearSignedPrekeySignature() => $_clearField(3);
|
void clearSignedPrekeySignature() => $_clearField(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ApplicationData_PqcPreKey extends $pb.GeneratedMessage {
|
||||||
|
factory ApplicationData_PqcPreKey({
|
||||||
|
$fixnum.Int64? eccPreKeyId,
|
||||||
|
$core.List<$core.int>? eccPreKey,
|
||||||
|
$fixnum.Int64? kyberPreKeyId,
|
||||||
|
$core.List<$core.int>? kyberPreKey,
|
||||||
|
$core.List<$core.int>? kyberPreKeySignature,
|
||||||
|
}) {
|
||||||
|
final result = create();
|
||||||
|
if (eccPreKeyId != null) result.eccPreKeyId = eccPreKeyId;
|
||||||
|
if (eccPreKey != null) result.eccPreKey = eccPreKey;
|
||||||
|
if (kyberPreKeyId != null) result.kyberPreKeyId = kyberPreKeyId;
|
||||||
|
if (kyberPreKey != null) result.kyberPreKey = kyberPreKey;
|
||||||
|
if (kyberPreKeySignature != null)
|
||||||
|
result.kyberPreKeySignature = kyberPreKeySignature;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationData_PqcPreKey._();
|
||||||
|
|
||||||
|
factory ApplicationData_PqcPreKey.fromBuffer($core.List<$core.int> data,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromBuffer(data, registry);
|
||||||
|
factory ApplicationData_PqcPreKey.fromJson($core.String json,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromJson(json, registry);
|
||||||
|
|
||||||
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||||
|
_omitMessageNames ? '' : 'ApplicationData.PqcPreKey',
|
||||||
|
package:
|
||||||
|
const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'),
|
||||||
|
createEmptyInstance: create)
|
||||||
|
..aInt64(1, _omitFieldNames ? '' : 'eccPreKeyId')
|
||||||
|
..a<$core.List<$core.int>>(
|
||||||
|
2, _omitFieldNames ? '' : 'eccPreKey', $pb.PbFieldType.OY)
|
||||||
|
..aInt64(3, _omitFieldNames ? '' : 'kyberPreKeyId')
|
||||||
|
..a<$core.List<$core.int>>(
|
||||||
|
4, _omitFieldNames ? '' : 'kyberPreKey', $pb.PbFieldType.OY)
|
||||||
|
..a<$core.List<$core.int>>(
|
||||||
|
5, _omitFieldNames ? '' : 'kyberPreKeySignature', $pb.PbFieldType.OY)
|
||||||
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
ApplicationData_PqcPreKey clone() => deepCopy();
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
ApplicationData_PqcPreKey copyWith(
|
||||||
|
void Function(ApplicationData_PqcPreKey) updates) =>
|
||||||
|
super.copyWith((message) => updates(message as ApplicationData_PqcPreKey))
|
||||||
|
as ApplicationData_PqcPreKey;
|
||||||
|
|
||||||
|
@$core.override
|
||||||
|
$pb.BuilderInfo get info_ => _i;
|
||||||
|
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static ApplicationData_PqcPreKey create() => ApplicationData_PqcPreKey._();
|
||||||
|
@$core.override
|
||||||
|
ApplicationData_PqcPreKey createEmptyInstance() => create();
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static ApplicationData_PqcPreKey getDefault() => _defaultInstance ??=
|
||||||
|
$pb.GeneratedMessage.$_defaultFor<ApplicationData_PqcPreKey>(create);
|
||||||
|
static ApplicationData_PqcPreKey? _defaultInstance;
|
||||||
|
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$fixnum.Int64 get eccPreKeyId => $_getI64(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
set eccPreKeyId($fixnum.Int64 value) => $_setInt64(0, value);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$core.bool hasEccPreKeyId() => $_has(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
void clearEccPreKeyId() => $_clearField(1);
|
||||||
|
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$core.List<$core.int> get eccPreKey => $_getN(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
set eccPreKey($core.List<$core.int> value) => $_setBytes(1, value);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$core.bool hasEccPreKey() => $_has(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
void clearEccPreKey() => $_clearField(2);
|
||||||
|
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$fixnum.Int64 get kyberPreKeyId => $_getI64(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
set kyberPreKeyId($fixnum.Int64 value) => $_setInt64(2, value);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$core.bool hasKyberPreKeyId() => $_has(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
void clearKyberPreKeyId() => $_clearField(3);
|
||||||
|
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$core.List<$core.int> get kyberPreKey => $_getN(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
set kyberPreKey($core.List<$core.int> value) => $_setBytes(3, value);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$core.bool hasKyberPreKey() => $_has(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
void clearKyberPreKey() => $_clearField(4);
|
||||||
|
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
$core.List<$core.int> get kyberPreKeySignature => $_getN(4);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
set kyberPreKeySignature($core.List<$core.int> value) => $_setBytes(4, value);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
$core.bool hasKyberPreKeySignature() => $_has(4);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
void clearKyberPreKeySignature() => $_clearField(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ApplicationData_UploadPqcPreKeys extends $pb.GeneratedMessage {
|
||||||
|
factory ApplicationData_UploadPqcPreKeys({
|
||||||
|
$fixnum.Int64? eccSignedPrekeyId,
|
||||||
|
$core.List<$core.int>? eccSignedPrekey,
|
||||||
|
$core.List<$core.int>? eccSignedPrekeySignature,
|
||||||
|
$fixnum.Int64? kyberSignedPrekeyId,
|
||||||
|
$core.List<$core.int>? kyberSignedPrekey,
|
||||||
|
$core.List<$core.int>? kyberSignedPrekeySignature,
|
||||||
|
$core.Iterable<ApplicationData_PqcPreKey>? prekeys,
|
||||||
|
}) {
|
||||||
|
final result = create();
|
||||||
|
if (eccSignedPrekeyId != null) result.eccSignedPrekeyId = eccSignedPrekeyId;
|
||||||
|
if (eccSignedPrekey != null) result.eccSignedPrekey = eccSignedPrekey;
|
||||||
|
if (eccSignedPrekeySignature != null)
|
||||||
|
result.eccSignedPrekeySignature = eccSignedPrekeySignature;
|
||||||
|
if (kyberSignedPrekeyId != null)
|
||||||
|
result.kyberSignedPrekeyId = kyberSignedPrekeyId;
|
||||||
|
if (kyberSignedPrekey != null) result.kyberSignedPrekey = kyberSignedPrekey;
|
||||||
|
if (kyberSignedPrekeySignature != null)
|
||||||
|
result.kyberSignedPrekeySignature = kyberSignedPrekeySignature;
|
||||||
|
if (prekeys != null) result.prekeys.addAll(prekeys);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationData_UploadPqcPreKeys._();
|
||||||
|
|
||||||
|
factory ApplicationData_UploadPqcPreKeys.fromBuffer(
|
||||||
|
$core.List<$core.int> data,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromBuffer(data, registry);
|
||||||
|
factory ApplicationData_UploadPqcPreKeys.fromJson($core.String json,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromJson(json, registry);
|
||||||
|
|
||||||
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||||
|
_omitMessageNames ? '' : 'ApplicationData.UploadPqcPreKeys',
|
||||||
|
package:
|
||||||
|
const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'),
|
||||||
|
createEmptyInstance: create)
|
||||||
|
..aInt64(1, _omitFieldNames ? '' : 'eccSignedPrekeyId')
|
||||||
|
..a<$core.List<$core.int>>(
|
||||||
|
2, _omitFieldNames ? '' : 'eccSignedPrekey', $pb.PbFieldType.OY)
|
||||||
|
..a<$core.List<$core.int>>(3,
|
||||||
|
_omitFieldNames ? '' : 'eccSignedPrekeySignature', $pb.PbFieldType.OY)
|
||||||
|
..aInt64(4, _omitFieldNames ? '' : 'kyberSignedPrekeyId')
|
||||||
|
..a<$core.List<$core.int>>(
|
||||||
|
5, _omitFieldNames ? '' : 'kyberSignedPrekey', $pb.PbFieldType.OY)
|
||||||
|
..a<$core.List<$core.int>>(6,
|
||||||
|
_omitFieldNames ? '' : 'kyberSignedPrekeySignature', $pb.PbFieldType.OY)
|
||||||
|
..pPM<ApplicationData_PqcPreKey>(7, _omitFieldNames ? '' : 'prekeys',
|
||||||
|
subBuilder: ApplicationData_PqcPreKey.create)
|
||||||
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
ApplicationData_UploadPqcPreKeys clone() => deepCopy();
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
ApplicationData_UploadPqcPreKeys copyWith(
|
||||||
|
void Function(ApplicationData_UploadPqcPreKeys) updates) =>
|
||||||
|
super.copyWith(
|
||||||
|
(message) => updates(message as ApplicationData_UploadPqcPreKeys))
|
||||||
|
as ApplicationData_UploadPqcPreKeys;
|
||||||
|
|
||||||
|
@$core.override
|
||||||
|
$pb.BuilderInfo get info_ => _i;
|
||||||
|
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static ApplicationData_UploadPqcPreKeys create() =>
|
||||||
|
ApplicationData_UploadPqcPreKeys._();
|
||||||
|
@$core.override
|
||||||
|
ApplicationData_UploadPqcPreKeys createEmptyInstance() => create();
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static ApplicationData_UploadPqcPreKeys getDefault() => _defaultInstance ??=
|
||||||
|
$pb.GeneratedMessage.$_defaultFor<ApplicationData_UploadPqcPreKeys>(
|
||||||
|
create);
|
||||||
|
static ApplicationData_UploadPqcPreKeys? _defaultInstance;
|
||||||
|
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$fixnum.Int64 get eccSignedPrekeyId => $_getI64(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
set eccSignedPrekeyId($fixnum.Int64 value) => $_setInt64(0, value);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$core.bool hasEccSignedPrekeyId() => $_has(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
void clearEccSignedPrekeyId() => $_clearField(1);
|
||||||
|
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$core.List<$core.int> get eccSignedPrekey => $_getN(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
set eccSignedPrekey($core.List<$core.int> value) => $_setBytes(1, value);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$core.bool hasEccSignedPrekey() => $_has(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
void clearEccSignedPrekey() => $_clearField(2);
|
||||||
|
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$core.List<$core.int> get eccSignedPrekeySignature => $_getN(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
set eccSignedPrekeySignature($core.List<$core.int> value) =>
|
||||||
|
$_setBytes(2, value);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$core.bool hasEccSignedPrekeySignature() => $_has(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
void clearEccSignedPrekeySignature() => $_clearField(3);
|
||||||
|
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$fixnum.Int64 get kyberSignedPrekeyId => $_getI64(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
set kyberSignedPrekeyId($fixnum.Int64 value) => $_setInt64(3, value);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$core.bool hasKyberSignedPrekeyId() => $_has(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
void clearKyberSignedPrekeyId() => $_clearField(4);
|
||||||
|
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
$core.List<$core.int> get kyberSignedPrekey => $_getN(4);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
set kyberSignedPrekey($core.List<$core.int> value) => $_setBytes(4, value);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
$core.bool hasKyberSignedPrekey() => $_has(4);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
void clearKyberSignedPrekey() => $_clearField(5);
|
||||||
|
|
||||||
|
@$pb.TagNumber(6)
|
||||||
|
$core.List<$core.int> get kyberSignedPrekeySignature => $_getN(5);
|
||||||
|
@$pb.TagNumber(6)
|
||||||
|
set kyberSignedPrekeySignature($core.List<$core.int> value) =>
|
||||||
|
$_setBytes(5, value);
|
||||||
|
@$pb.TagNumber(6)
|
||||||
|
$core.bool hasKyberSignedPrekeySignature() => $_has(5);
|
||||||
|
@$pb.TagNumber(6)
|
||||||
|
void clearKyberSignedPrekeySignature() => $_clearField(6);
|
||||||
|
|
||||||
|
@$pb.TagNumber(7)
|
||||||
|
$pb.PbList<ApplicationData_PqcPreKey> get prekeys => $_getList(6);
|
||||||
|
}
|
||||||
|
|
||||||
class ApplicationData_DownloadDone extends $pb.GeneratedMessage {
|
class ApplicationData_DownloadDone extends $pb.GeneratedMessage {
|
||||||
factory ApplicationData_DownloadDone({
|
factory ApplicationData_DownloadDone({
|
||||||
$core.List<$core.int>? downloadToken,
|
$core.List<$core.int>? downloadToken,
|
||||||
|
|
@ -3085,6 +3329,7 @@ enum ApplicationData_ApplicationData {
|
||||||
getMemoriesUsage,
|
getMemoriesUsage,
|
||||||
deleteMemory,
|
deleteMemory,
|
||||||
disableMemoriesBackup,
|
disableMemoriesBackup,
|
||||||
|
uploadPqcPrekeys,
|
||||||
notSet
|
notSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3117,6 +3362,7 @@ class ApplicationData extends $pb.GeneratedMessage {
|
||||||
ApplicationData_GetMemoriesUsage? getMemoriesUsage,
|
ApplicationData_GetMemoriesUsage? getMemoriesUsage,
|
||||||
ApplicationData_DeleteMemory? deleteMemory,
|
ApplicationData_DeleteMemory? deleteMemory,
|
||||||
ApplicationData_DisableMemoriesBackup? disableMemoriesBackup,
|
ApplicationData_DisableMemoriesBackup? disableMemoriesBackup,
|
||||||
|
ApplicationData_UploadPqcPreKeys? uploadPqcPrekeys,
|
||||||
}) {
|
}) {
|
||||||
final result = create();
|
final result = create();
|
||||||
if (textMessage != null) result.textMessage = textMessage;
|
if (textMessage != null) result.textMessage = textMessage;
|
||||||
|
|
@ -3157,6 +3403,7 @@ class ApplicationData extends $pb.GeneratedMessage {
|
||||||
if (deleteMemory != null) result.deleteMemory = deleteMemory;
|
if (deleteMemory != null) result.deleteMemory = deleteMemory;
|
||||||
if (disableMemoriesBackup != null)
|
if (disableMemoriesBackup != null)
|
||||||
result.disableMemoriesBackup = disableMemoriesBackup;
|
result.disableMemoriesBackup = disableMemoriesBackup;
|
||||||
|
if (uploadPqcPrekeys != null) result.uploadPqcPrekeys = uploadPqcPrekeys;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3198,6 +3445,7 @@ class ApplicationData extends $pb.GeneratedMessage {
|
||||||
37: ApplicationData_ApplicationData.getMemoriesUsage,
|
37: ApplicationData_ApplicationData.getMemoriesUsage,
|
||||||
38: ApplicationData_ApplicationData.deleteMemory,
|
38: ApplicationData_ApplicationData.deleteMemory,
|
||||||
39: ApplicationData_ApplicationData.disableMemoriesBackup,
|
39: ApplicationData_ApplicationData.disableMemoriesBackup,
|
||||||
|
40: ApplicationData_ApplicationData.uploadPqcPrekeys,
|
||||||
0: ApplicationData_ApplicationData.notSet
|
0: ApplicationData_ApplicationData.notSet
|
||||||
};
|
};
|
||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||||
|
|
@ -3232,7 +3480,8 @@ class ApplicationData extends $pb.GeneratedMessage {
|
||||||
36,
|
36,
|
||||||
37,
|
37,
|
||||||
38,
|
38,
|
||||||
39
|
39,
|
||||||
|
40
|
||||||
])
|
])
|
||||||
..aOM<ApplicationData_TextMessage>(1, _omitFieldNames ? '' : 'textMessage',
|
..aOM<ApplicationData_TextMessage>(1, _omitFieldNames ? '' : 'textMessage',
|
||||||
protoName: 'textMessage',
|
protoName: 'textMessage',
|
||||||
|
|
@ -3327,6 +3576,9 @@ class ApplicationData extends $pb.GeneratedMessage {
|
||||||
..aOM<ApplicationData_DisableMemoriesBackup>(
|
..aOM<ApplicationData_DisableMemoriesBackup>(
|
||||||
39, _omitFieldNames ? '' : 'disableMemoriesBackup',
|
39, _omitFieldNames ? '' : 'disableMemoriesBackup',
|
||||||
subBuilder: ApplicationData_DisableMemoriesBackup.create)
|
subBuilder: ApplicationData_DisableMemoriesBackup.create)
|
||||||
|
..aOM<ApplicationData_UploadPqcPreKeys>(
|
||||||
|
40, _omitFieldNames ? '' : 'uploadPqcPrekeys',
|
||||||
|
subBuilder: ApplicationData_UploadPqcPreKeys.create)
|
||||||
..hasRequiredFields = false;
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
|
@ -3375,6 +3627,7 @@ class ApplicationData extends $pb.GeneratedMessage {
|
||||||
@$pb.TagNumber(37)
|
@$pb.TagNumber(37)
|
||||||
@$pb.TagNumber(38)
|
@$pb.TagNumber(38)
|
||||||
@$pb.TagNumber(39)
|
@$pb.TagNumber(39)
|
||||||
|
@$pb.TagNumber(40)
|
||||||
ApplicationData_ApplicationData whichApplicationData() =>
|
ApplicationData_ApplicationData whichApplicationData() =>
|
||||||
_ApplicationData_ApplicationDataByTag[$_whichOneof(0)]!;
|
_ApplicationData_ApplicationDataByTag[$_whichOneof(0)]!;
|
||||||
@$pb.TagNumber(1)
|
@$pb.TagNumber(1)
|
||||||
|
|
@ -3404,6 +3657,7 @@ class ApplicationData extends $pb.GeneratedMessage {
|
||||||
@$pb.TagNumber(37)
|
@$pb.TagNumber(37)
|
||||||
@$pb.TagNumber(38)
|
@$pb.TagNumber(38)
|
||||||
@$pb.TagNumber(39)
|
@$pb.TagNumber(39)
|
||||||
|
@$pb.TagNumber(40)
|
||||||
void clearApplicationData() => $_clearField($_whichOneof(0));
|
void clearApplicationData() => $_clearField($_whichOneof(0));
|
||||||
|
|
||||||
@$pb.TagNumber(1)
|
@$pb.TagNumber(1)
|
||||||
|
|
@ -3737,6 +3991,18 @@ class ApplicationData extends $pb.GeneratedMessage {
|
||||||
@$pb.TagNumber(39)
|
@$pb.TagNumber(39)
|
||||||
ApplicationData_DisableMemoriesBackup ensureDisableMemoriesBackup() =>
|
ApplicationData_DisableMemoriesBackup ensureDisableMemoriesBackup() =>
|
||||||
$_ensure(26);
|
$_ensure(26);
|
||||||
|
|
||||||
|
@$pb.TagNumber(40)
|
||||||
|
ApplicationData_UploadPqcPreKeys get uploadPqcPrekeys => $_getN(27);
|
||||||
|
@$pb.TagNumber(40)
|
||||||
|
set uploadPqcPrekeys(ApplicationData_UploadPqcPreKeys value) =>
|
||||||
|
$_setField(40, value);
|
||||||
|
@$pb.TagNumber(40)
|
||||||
|
$core.bool hasUploadPqcPrekeys() => $_has(27);
|
||||||
|
@$pb.TagNumber(40)
|
||||||
|
void clearUploadPqcPrekeys() => $_clearField(40);
|
||||||
|
@$pb.TagNumber(40)
|
||||||
|
ApplicationData_UploadPqcPreKeys ensureUploadPqcPrekeys() => $_ensure(27);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Response_PreKey extends $pb.GeneratedMessage {
|
class Response_PreKey extends $pb.GeneratedMessage {
|
||||||
|
|
@ -3857,16 +4123,68 @@ class Response_Prekeys extends $pb.GeneratedMessage {
|
||||||
$pb.PbList<Response_PreKey> get prekeys => $_getList(0);
|
$pb.PbList<Response_PreKey> get prekeys => $_getList(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Response_Ok_Ok { none, prekeys, notSet }
|
class Response_PqcPrekeys extends $pb.GeneratedMessage {
|
||||||
|
factory Response_PqcPrekeys({
|
||||||
|
$core.Iterable<ApplicationData_PqcPreKey>? prekeys,
|
||||||
|
}) {
|
||||||
|
final result = create();
|
||||||
|
if (prekeys != null) result.prekeys.addAll(prekeys);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Response_PqcPrekeys._();
|
||||||
|
|
||||||
|
factory Response_PqcPrekeys.fromBuffer($core.List<$core.int> data,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromBuffer(data, registry);
|
||||||
|
factory Response_PqcPrekeys.fromJson($core.String json,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromJson(json, registry);
|
||||||
|
|
||||||
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||||
|
_omitMessageNames ? '' : 'Response.PqcPrekeys',
|
||||||
|
package:
|
||||||
|
const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'),
|
||||||
|
createEmptyInstance: create)
|
||||||
|
..pPM<ApplicationData_PqcPreKey>(1, _omitFieldNames ? '' : 'prekeys',
|
||||||
|
subBuilder: ApplicationData_PqcPreKey.create)
|
||||||
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
Response_PqcPrekeys clone() => deepCopy();
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
Response_PqcPrekeys copyWith(void Function(Response_PqcPrekeys) updates) =>
|
||||||
|
super.copyWith((message) => updates(message as Response_PqcPrekeys))
|
||||||
|
as Response_PqcPrekeys;
|
||||||
|
|
||||||
|
@$core.override
|
||||||
|
$pb.BuilderInfo get info_ => _i;
|
||||||
|
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static Response_PqcPrekeys create() => Response_PqcPrekeys._();
|
||||||
|
@$core.override
|
||||||
|
Response_PqcPrekeys createEmptyInstance() => create();
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static Response_PqcPrekeys getDefault() => _defaultInstance ??=
|
||||||
|
$pb.GeneratedMessage.$_defaultFor<Response_PqcPrekeys>(create);
|
||||||
|
static Response_PqcPrekeys? _defaultInstance;
|
||||||
|
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$pb.PbList<ApplicationData_PqcPreKey> get prekeys => $_getList(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Response_Ok_Ok { none, prekeys, prekeysPqc, notSet }
|
||||||
|
|
||||||
class Response_Ok extends $pb.GeneratedMessage {
|
class Response_Ok extends $pb.GeneratedMessage {
|
||||||
factory Response_Ok({
|
factory Response_Ok({
|
||||||
$core.bool? none,
|
$core.bool? none,
|
||||||
Response_Prekeys? prekeys,
|
Response_Prekeys? prekeys,
|
||||||
|
Response_PqcPrekeys? prekeysPqc,
|
||||||
}) {
|
}) {
|
||||||
final result = create();
|
final result = create();
|
||||||
if (none != null) result.none = none;
|
if (none != null) result.none = none;
|
||||||
if (prekeys != null) result.prekeys = prekeys;
|
if (prekeys != null) result.prekeys = prekeys;
|
||||||
|
if (prekeysPqc != null) result.prekeysPqc = prekeysPqc;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3882,6 +4200,7 @@ class Response_Ok extends $pb.GeneratedMessage {
|
||||||
static const $core.Map<$core.int, Response_Ok_Ok> _Response_Ok_OkByTag = {
|
static const $core.Map<$core.int, Response_Ok_Ok> _Response_Ok_OkByTag = {
|
||||||
1: Response_Ok_Ok.none,
|
1: Response_Ok_Ok.none,
|
||||||
2: Response_Ok_Ok.prekeys,
|
2: Response_Ok_Ok.prekeys,
|
||||||
|
3: Response_Ok_Ok.prekeysPqc,
|
||||||
0: Response_Ok_Ok.notSet
|
0: Response_Ok_Ok.notSet
|
||||||
};
|
};
|
||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||||
|
|
@ -3889,10 +4208,12 @@ class Response_Ok extends $pb.GeneratedMessage {
|
||||||
package:
|
package:
|
||||||
const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'),
|
const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'),
|
||||||
createEmptyInstance: create)
|
createEmptyInstance: create)
|
||||||
..oo(0, [1, 2])
|
..oo(0, [1, 2, 3])
|
||||||
..aOB(1, _omitFieldNames ? '' : 'None', protoName: 'None')
|
..aOB(1, _omitFieldNames ? '' : 'None', protoName: 'None')
|
||||||
..aOM<Response_Prekeys>(2, _omitFieldNames ? '' : 'prekeys',
|
..aOM<Response_Prekeys>(2, _omitFieldNames ? '' : 'prekeys',
|
||||||
subBuilder: Response_Prekeys.create)
|
subBuilder: Response_Prekeys.create)
|
||||||
|
..aOM<Response_PqcPrekeys>(3, _omitFieldNames ? '' : 'prekeysPqc',
|
||||||
|
subBuilder: Response_PqcPrekeys.create)
|
||||||
..hasRequiredFields = false;
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
|
@ -3916,9 +4237,11 @@ class Response_Ok extends $pb.GeneratedMessage {
|
||||||
|
|
||||||
@$pb.TagNumber(1)
|
@$pb.TagNumber(1)
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
|
@$pb.TagNumber(3)
|
||||||
Response_Ok_Ok whichOk() => _Response_Ok_OkByTag[$_whichOneof(0)]!;
|
Response_Ok_Ok whichOk() => _Response_Ok_OkByTag[$_whichOneof(0)]!;
|
||||||
@$pb.TagNumber(1)
|
@$pb.TagNumber(1)
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
|
@$pb.TagNumber(3)
|
||||||
void clearOk() => $_clearField($_whichOneof(0));
|
void clearOk() => $_clearField($_whichOneof(0));
|
||||||
|
|
||||||
@$pb.TagNumber(1)
|
@$pb.TagNumber(1)
|
||||||
|
|
@ -3940,6 +4263,17 @@ class Response_Ok extends $pb.GeneratedMessage {
|
||||||
void clearPrekeys() => $_clearField(2);
|
void clearPrekeys() => $_clearField(2);
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
Response_Prekeys ensurePrekeys() => $_ensure(1);
|
Response_Prekeys ensurePrekeys() => $_ensure(1);
|
||||||
|
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
Response_PqcPrekeys get prekeysPqc => $_getN(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
set prekeysPqc(Response_PqcPrekeys value) => $_setField(3, value);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$core.bool hasPrekeysPqc() => $_has(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
void clearPrekeysPqc() => $_clearField(3);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
Response_PqcPrekeys ensurePrekeysPqc() => $_ensure(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Response_Response { ok, error, notSet }
|
enum Response_Response { ok, error, notSet }
|
||||||
|
|
|
||||||
|
|
@ -724,6 +724,15 @@ const ApplicationData$json = {
|
||||||
'9': 0,
|
'9': 0,
|
||||||
'10': 'disableMemoriesBackup'
|
'10': 'disableMemoriesBackup'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'1': 'upload_pqc_prekeys',
|
||||||
|
'3': 40,
|
||||||
|
'4': 1,
|
||||||
|
'5': 11,
|
||||||
|
'6': '.client_to_server.ApplicationData.UploadPqcPreKeys',
|
||||||
|
'9': 0,
|
||||||
|
'10': 'uploadPqcPrekeys'
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'3': [
|
'3': [
|
||||||
ApplicationData_TextMessage$json,
|
ApplicationData_TextMessage$json,
|
||||||
|
|
@ -737,6 +746,8 @@ const ApplicationData$json = {
|
||||||
ApplicationData_GetPrekeysByUserId$json,
|
ApplicationData_GetPrekeysByUserId$json,
|
||||||
ApplicationData_GetSignedPreKeyByUserId$json,
|
ApplicationData_GetSignedPreKeyByUserId$json,
|
||||||
ApplicationData_UpdateSignedPreKey$json,
|
ApplicationData_UpdateSignedPreKey$json,
|
||||||
|
ApplicationData_PqcPreKey$json,
|
||||||
|
ApplicationData_UploadPqcPreKeys$json,
|
||||||
ApplicationData_DownloadDone$json,
|
ApplicationData_DownloadDone$json,
|
||||||
ApplicationData_ReportUser$json,
|
ApplicationData_ReportUser$json,
|
||||||
ApplicationData_IPAPurchase$json,
|
ApplicationData_IPAPurchase$json,
|
||||||
|
|
@ -868,6 +879,81 @@ const ApplicationData_UpdateSignedPreKey$json = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use applicationDataDescriptor instead')
|
||||||
|
const ApplicationData_PqcPreKey$json = {
|
||||||
|
'1': 'PqcPreKey',
|
||||||
|
'2': [
|
||||||
|
{'1': 'ecc_pre_key_id', '3': 1, '4': 1, '5': 3, '10': 'eccPreKeyId'},
|
||||||
|
{'1': 'ecc_pre_key', '3': 2, '4': 1, '5': 12, '10': 'eccPreKey'},
|
||||||
|
{'1': 'kyber_pre_key_id', '3': 3, '4': 1, '5': 3, '10': 'kyberPreKeyId'},
|
||||||
|
{'1': 'kyber_pre_key', '3': 4, '4': 1, '5': 12, '10': 'kyberPreKey'},
|
||||||
|
{
|
||||||
|
'1': 'kyber_pre_key_signature',
|
||||||
|
'3': 5,
|
||||||
|
'4': 1,
|
||||||
|
'5': 12,
|
||||||
|
'10': 'kyberPreKeySignature'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use applicationDataDescriptor instead')
|
||||||
|
const ApplicationData_UploadPqcPreKeys$json = {
|
||||||
|
'1': 'UploadPqcPreKeys',
|
||||||
|
'2': [
|
||||||
|
{
|
||||||
|
'1': 'ecc_signed_prekey_id',
|
||||||
|
'3': 1,
|
||||||
|
'4': 1,
|
||||||
|
'5': 3,
|
||||||
|
'10': 'eccSignedPrekeyId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'1': 'ecc_signed_prekey',
|
||||||
|
'3': 2,
|
||||||
|
'4': 1,
|
||||||
|
'5': 12,
|
||||||
|
'10': 'eccSignedPrekey'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'1': 'ecc_signed_prekey_signature',
|
||||||
|
'3': 3,
|
||||||
|
'4': 1,
|
||||||
|
'5': 12,
|
||||||
|
'10': 'eccSignedPrekeySignature'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'1': 'kyber_signed_prekey_id',
|
||||||
|
'3': 4,
|
||||||
|
'4': 1,
|
||||||
|
'5': 3,
|
||||||
|
'10': 'kyberSignedPrekeyId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'1': 'kyber_signed_prekey',
|
||||||
|
'3': 5,
|
||||||
|
'4': 1,
|
||||||
|
'5': 12,
|
||||||
|
'10': 'kyberSignedPrekey'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'1': 'kyber_signed_prekey_signature',
|
||||||
|
'3': 6,
|
||||||
|
'4': 1,
|
||||||
|
'5': 12,
|
||||||
|
'10': 'kyberSignedPrekeySignature'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'1': 'prekeys',
|
||||||
|
'3': 7,
|
||||||
|
'4': 3,
|
||||||
|
'5': 11,
|
||||||
|
'6': '.client_to_server.ApplicationData.PqcPreKey',
|
||||||
|
'10': 'prekeys'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
@$core.Deprecated('Use applicationDataDescriptor instead')
|
@$core.Deprecated('Use applicationDataDescriptor instead')
|
||||||
const ApplicationData_DownloadDone$json = {
|
const ApplicationData_DownloadDone$json = {
|
||||||
'1': 'DownloadDone',
|
'1': 'DownloadDone',
|
||||||
|
|
@ -1073,38 +1159,51 @@ final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode(
|
||||||
'CzIuLmNsaWVudF90b19zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLkRlbGV0ZU1lbW9yeUgAUgxkZW'
|
'CzIuLmNsaWVudF90b19zZXJ2ZXIuQXBwbGljYXRpb25EYXRhLkRlbGV0ZU1lbW9yeUgAUgxkZW'
|
||||||
'xldGVNZW1vcnkScQoXZGlzYWJsZV9tZW1vcmllc19iYWNrdXAYJyABKAsyNy5jbGllbnRfdG9f'
|
'xldGVNZW1vcnkScQoXZGlzYWJsZV9tZW1vcmllc19iYWNrdXAYJyABKAsyNy5jbGllbnRfdG9f'
|
||||||
'c2VydmVyLkFwcGxpY2F0aW9uRGF0YS5EaXNhYmxlTWVtb3JpZXNCYWNrdXBIAFIVZGlzYWJsZU'
|
'c2VydmVyLkFwcGxpY2F0aW9uRGF0YS5EaXNhYmxlTWVtb3JpZXNCYWNrdXBIAFIVZGlzYWJsZU'
|
||||||
'1lbW9yaWVzQmFja3VwGmoKC1RleHRNZXNzYWdlEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBIS'
|
'1lbW9yaWVzQmFja3VwEmIKEnVwbG9hZF9wcWNfcHJla2V5cxgoIAEoCzIyLmNsaWVudF90b19z'
|
||||||
'CgRib2R5GAMgASgMUgRib2R5EiAKCXB1c2hfZGF0YRgEIAEoDEgAUghwdXNoRGF0YYgBAUIMCg'
|
'ZXJ2ZXIuQXBwbGljYXRpb25EYXRhLlVwbG9hZFBxY1ByZUtleXNIAFIQdXBsb2FkUHFjUHJla2'
|
||||||
'pfcHVzaF9kYXRhGi8KEUdldFVzZXJCeVVzZXJuYW1lEhoKCHVzZXJuYW1lGAEgASgJUgh1c2Vy'
|
'V5cxpqCgtUZXh0TWVzc2FnZRIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQSEgoEYm9keRgDIAEo'
|
||||||
'bmFtZRosCg5DaGFuZ2VVc2VybmFtZRIaCgh1c2VybmFtZRgBIAEoCVIIdXNlcm5hbWUaNQoUVX'
|
'DFIEYm9keRIgCglwdXNoX2RhdGEYBCABKAxIAFIIcHVzaERhdGGIAQFCDAoKX3B1c2hfZGF0YR'
|
||||||
'BkYXRlR29vZ2xlRmNtVG9rZW4SHQoKZ29vZ2xlX2ZjbRgBIAEoCVIJZ29vZ2xlRmNtGiYKC0dl'
|
'ovChFHZXRVc2VyQnlVc2VybmFtZRIaCgh1c2VybmFtZRgBIAEoCVIIdXNlcm5hbWUaLAoOQ2hh'
|
||||||
'dFVzZXJCeUlkEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBoTChFHZXRBdmFpbGFibGVQbGFucx'
|
'bmdlVXNlcm5hbWUSGgoIdXNlcm5hbWUYASABKAlSCHVzZXJuYW1lGjUKFFVwZGF0ZUdvb2dsZU'
|
||||||
'oVChNHZXRDdXJyZW50UGxhbkluZm9zGi8KFFJlbW92ZUFkZGl0aW9uYWxVc2VyEhcKB3VzZXJf'
|
'ZjbVRva2VuEh0KCmdvb2dsZV9mY20YASABKAlSCWdvb2dsZUZjbRomCgtHZXRVc2VyQnlJZBIX'
|
||||||
'aWQYASABKANSBnVzZXJJZBotChJHZXRQcmVrZXlzQnlVc2VySWQSFwoHdXNlcl9pZBgBIAEoA1'
|
'Cgd1c2VyX2lkGAEgASgDUgZ1c2VySWQaEwoRR2V0QXZhaWxhYmxlUGxhbnMaFQoTR2V0Q3Vycm'
|
||||||
'IGdXNlcklkGjIKF0dldFNpZ25lZFByZUtleUJ5VXNlcklkEhcKB3VzZXJfaWQYASABKANSBnVz'
|
'VudFBsYW5JbmZvcxovChRSZW1vdmVBZGRpdGlvbmFsVXNlchIXCgd1c2VyX2lkGAEgASgDUgZ1'
|
||||||
'ZXJJZBqbAQoSVXBkYXRlU2lnbmVkUHJlS2V5EigKEHNpZ25lZF9wcmVrZXlfaWQYASABKANSDn'
|
'c2VySWQaLQoSR2V0UHJla2V5c0J5VXNlcklkEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBoyCh'
|
||||||
'NpZ25lZFByZWtleUlkEiMKDXNpZ25lZF9wcmVrZXkYAiABKAxSDHNpZ25lZFByZWtleRI2Chdz'
|
'dHZXRTaWduZWRQcmVLZXlCeVVzZXJJZBIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQamwEKElVw'
|
||||||
'aWduZWRfcHJla2V5X3NpZ25hdHVyZRgDIAEoDFIVc2lnbmVkUHJla2V5U2lnbmF0dXJlGjUKDE'
|
'ZGF0ZVNpZ25lZFByZUtleRIoChBzaWduZWRfcHJla2V5X2lkGAEgASgDUg5zaWduZWRQcmVrZX'
|
||||||
'Rvd25sb2FkRG9uZRIlCg5kb3dubG9hZF90b2tlbhgBIAEoDFINZG93bmxvYWRUb2tlbhpOCgpS'
|
'lJZBIjCg1zaWduZWRfcHJla2V5GAIgASgMUgxzaWduZWRQcmVrZXkSNgoXc2lnbmVkX3ByZWtl'
|
||||||
'ZXBvcnRVc2VyEigKEHJlcG9ydGVkX3VzZXJfaWQYASABKANSDnJlcG9ydGVkVXNlcklkEhYKBn'
|
'eV9zaWduYXR1cmUYAyABKAxSFXNpZ25lZFByZWtleVNpZ25hdHVyZRrUAQoJUHFjUHJlS2V5Ei'
|
||||||
'JlYXNvbhgCIAEoCVIGcmVhc29uGnEKC0lQQVB1cmNoYXNlEh0KCnByb2R1Y3RfaWQYASABKAlS'
|
'MKDmVjY19wcmVfa2V5X2lkGAEgASgDUgtlY2NQcmVLZXlJZBIeCgtlY2NfcHJlX2tleRgCIAEo'
|
||||||
'CXByb2R1Y3RJZBIWCgZzb3VyY2UYAiABKAlSBnNvdXJjZRIrChF2ZXJpZmljYXRpb25fZGF0YR'
|
'DFIJZWNjUHJlS2V5EicKEGt5YmVyX3ByZV9rZXlfaWQYAyABKANSDWt5YmVyUHJlS2V5SWQSIg'
|
||||||
'gDIAEoCVIQdmVyaWZpY2F0aW9uRGF0YRoPCg1JUEFGb3JjZUNoZWNrGg8KDURlbGV0ZUFjY291'
|
'oNa3liZXJfcHJlX2tleRgEIAEoDFILa3liZXJQcmVLZXkSNQoXa3liZXJfcHJlX2tleV9zaWdu'
|
||||||
'bnQaLAoRQWRkQWRkaXRpb25hbFVzZXISFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkGjAKDVNldE'
|
'YXR1cmUYBSABKAxSFGt5YmVyUHJlS2V5U2lnbmF0dXJlGp0DChBVcGxvYWRQcWNQcmVLZXlzEi'
|
||||||
'xvZ2luVG9rZW4SHwoLbG9naW5fdG9rZW4YASABKAxSCmxvZ2luVG9rZW4ajgEKHFJlZ2lzdGVy'
|
'8KFGVjY19zaWduZWRfcHJla2V5X2lkGAEgASgDUhFlY2NTaWduZWRQcmVrZXlJZBIqChFlY2Nf'
|
||||||
'UGFzc3dvcmRMZXNzUmVjb3ZlcnkSLgoSZW5jcnlwdGVkU2VydmVyS2V5GAEgASgMUhJlbmNyeX'
|
'c2lnbmVkX3ByZWtleRgCIAEoDFIPZWNjU2lnbmVkUHJla2V5Ej0KG2VjY19zaWduZWRfcHJla2'
|
||||||
'B0ZWRTZXJ2ZXJLZXkSKwoOcGluVW5sb2NrVG9rZW4YAiABKAxIAFIOcGluVW5sb2NrVG9rZW6I'
|
'V5X3NpZ25hdHVyZRgDIAEoDFIYZWNjU2lnbmVkUHJla2V5U2lnbmF0dXJlEjMKFmt5YmVyX3Np'
|
||||||
'AQFCEQoPX3BpblVubG9ja1Rva2VuGnAKGFBhc3N3b3JkbGVzc05vdGlmaWNhdGlvbhInCg9ub3'
|
'Z25lZF9wcmVrZXlfaWQYBCABKANSE2t5YmVyU2lnbmVkUHJla2V5SWQSLgoTa3liZXJfc2lnbm'
|
||||||
'RpZmljYXRpb25faWQYASABKAlSDm5vdGlmaWNhdGlvbklkEisKEWVuY3J5cHRlZF9tZXNzYWdl'
|
'VkX3ByZWtleRgFIAEoDFIRa3liZXJTaWduZWRQcmVrZXkSQQoda3liZXJfc2lnbmVkX3ByZWtl'
|
||||||
'GAIgASgMUhBlbmNyeXB0ZWRNZXNzYWdlGmsKFVJlcXVlc3RNZW1vcmllc1VwbG9hZBISCgRzaX'
|
'eV9zaWduYXR1cmUYBiABKAxSGmt5YmVyU2lnbmVkUHJla2V5U2lnbmF0dXJlEkUKB3ByZWtleX'
|
||||||
'plGAEgASgDUgRzaXplEiMKDW9yaWdpbmFsX2RhdGUYAiABKANSDG9yaWdpbmFsRGF0ZRIZCght'
|
'MYByADKAsyKy5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5QcWNQcmVLZXlSB3By'
|
||||||
'ZWRpYV9pZBgDIAEoCVIHbWVkaWFJZBoyChVDb25maXJtTWVtb3JpZXNVcGxvYWQSGQoIbWVkaW'
|
'ZWtleXMaNQoMRG93bmxvYWREb25lEiUKDmRvd25sb2FkX3Rva2VuGAEgASgMUg1kb3dubG9hZF'
|
||||||
'FfaWQYASABKAlSB21lZGlhSWQaSAoPR2V0TWVtb3JpZXNMaXN0Eh8KC29mZnNldF9kYXRlGAEg'
|
'Rva2VuGk4KClJlcG9ydFVzZXISKAoQcmVwb3J0ZWRfdXNlcl9pZBgBIAEoA1IOcmVwb3J0ZWRV'
|
||||||
'ASgDUgpvZmZzZXREYXRlEhQKBWxpbWl0GAIgASgDUgVsaW1pdBpJCg5HZXRNZW1vcmllc1VybB'
|
'c2VySWQSFgoGcmVhc29uGAIgASgJUgZyZWFzb24acQoLSVBBUHVyY2hhc2USHQoKcHJvZHVjdF'
|
||||||
'IZCghtZWRpYV9pZBgBIAEoCVIHbWVkaWFJZBIcCgl0aHVtYm5haWwYAiABKAhSCXRodW1ibmFp'
|
'9pZBgBIAEoCVIJcHJvZHVjdElkEhYKBnNvdXJjZRgCIAEoCVIGc291cmNlEisKEXZlcmlmaWNh'
|
||||||
'bBoSChBHZXRNZW1vcmllc1VzYWdlGikKDERlbGV0ZU1lbW9yeRIZCghtZWRpYV9pZBgBIAEoCV'
|
'dGlvbl9kYXRhGAMgASgJUhB2ZXJpZmljYXRpb25EYXRhGg8KDUlQQUZvcmNlQ2hlY2saDwoNRG'
|
||||||
'IHbWVkaWFJZBoXChVEaXNhYmxlTWVtb3JpZXNCYWNrdXBCEQoPQXBwbGljYXRpb25EYXRhSgQI'
|
'VsZXRlQWNjb3VudBosChFBZGRBZGRpdGlvbmFsVXNlchIXCgd1c2VyX2lkGAEgASgDUgZ1c2Vy'
|
||||||
'CRAKSgQICxAMSgQIDRASSgQIExAU');
|
'SWQaMAoNU2V0TG9naW5Ub2tlbhIfCgtsb2dpbl90b2tlbhgBIAEoDFIKbG9naW5Ub2tlbhqOAQ'
|
||||||
|
'ocUmVnaXN0ZXJQYXNzd29yZExlc3NSZWNvdmVyeRIuChJlbmNyeXB0ZWRTZXJ2ZXJLZXkYASAB'
|
||||||
|
'KAxSEmVuY3J5cHRlZFNlcnZlcktleRIrCg5waW5VbmxvY2tUb2tlbhgCIAEoDEgAUg5waW5Vbm'
|
||||||
|
'xvY2tUb2tlbogBAUIRCg9fcGluVW5sb2NrVG9rZW4acAoYUGFzc3dvcmRsZXNzTm90aWZpY2F0'
|
||||||
|
'aW9uEicKD25vdGlmaWNhdGlvbl9pZBgBIAEoCVIObm90aWZpY2F0aW9uSWQSKwoRZW5jcnlwdG'
|
||||||
|
'VkX21lc3NhZ2UYAiABKAxSEGVuY3J5cHRlZE1lc3NhZ2UaawoVUmVxdWVzdE1lbW9yaWVzVXBs'
|
||||||
|
'b2FkEhIKBHNpemUYASABKANSBHNpemUSIwoNb3JpZ2luYWxfZGF0ZRgCIAEoA1IMb3JpZ2luYW'
|
||||||
|
'xEYXRlEhkKCG1lZGlhX2lkGAMgASgJUgdtZWRpYUlkGjIKFUNvbmZpcm1NZW1vcmllc1VwbG9h'
|
||||||
|
'ZBIZCghtZWRpYV9pZBgBIAEoCVIHbWVkaWFJZBpICg9HZXRNZW1vcmllc0xpc3QSHwoLb2Zmc2'
|
||||||
|
'V0X2RhdGUYASABKANSCm9mZnNldERhdGUSFAoFbGltaXQYAiABKANSBWxpbWl0GkkKDkdldE1l'
|
||||||
|
'bW9yaWVzVXJsEhkKCG1lZGlhX2lkGAEgASgJUgdtZWRpYUlkEhwKCXRodW1ibmFpbBgCIAEoCF'
|
||||||
|
'IJdGh1bWJuYWlsGhIKEEdldE1lbW9yaWVzVXNhZ2UaKQoMRGVsZXRlTWVtb3J5EhkKCG1lZGlh'
|
||||||
|
'X2lkGAEgASgJUgdtZWRpYUlkGhcKFURpc2FibGVNZW1vcmllc0JhY2t1cEIRCg9BcHBsaWNhdG'
|
||||||
|
'lvbkRhdGFKBAgJEApKBAgLEAxKBAgNEBJKBAgTEBQ=');
|
||||||
|
|
||||||
@$core.Deprecated('Use responseDescriptor instead')
|
@$core.Deprecated('Use responseDescriptor instead')
|
||||||
const Response$json = {
|
const Response$json = {
|
||||||
|
|
@ -1129,7 +1228,12 @@ const Response$json = {
|
||||||
'10': 'error'
|
'10': 'error'
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'3': [Response_PreKey$json, Response_Prekeys$json, Response_Ok$json],
|
'3': [
|
||||||
|
Response_PreKey$json,
|
||||||
|
Response_Prekeys$json,
|
||||||
|
Response_PqcPrekeys$json,
|
||||||
|
Response_Ok$json
|
||||||
|
],
|
||||||
'8': [
|
'8': [
|
||||||
{'1': 'Response'},
|
{'1': 'Response'},
|
||||||
],
|
],
|
||||||
|
|
@ -1159,6 +1263,21 @@ const Response_Prekeys$json = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use responseDescriptor instead')
|
||||||
|
const Response_PqcPrekeys$json = {
|
||||||
|
'1': 'PqcPrekeys',
|
||||||
|
'2': [
|
||||||
|
{
|
||||||
|
'1': 'prekeys',
|
||||||
|
'3': 1,
|
||||||
|
'4': 3,
|
||||||
|
'5': 11,
|
||||||
|
'6': '.client_to_server.ApplicationData.PqcPreKey',
|
||||||
|
'10': 'prekeys'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
@$core.Deprecated('Use responseDescriptor instead')
|
@$core.Deprecated('Use responseDescriptor instead')
|
||||||
const Response_Ok$json = {
|
const Response_Ok$json = {
|
||||||
'1': 'Ok',
|
'1': 'Ok',
|
||||||
|
|
@ -1173,6 +1292,15 @@ const Response_Ok$json = {
|
||||||
'9': 0,
|
'9': 0,
|
||||||
'10': 'prekeys'
|
'10': 'prekeys'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'1': 'prekeys_pqc',
|
||||||
|
'3': 3,
|
||||||
|
'4': 1,
|
||||||
|
'5': 11,
|
||||||
|
'6': '.client_to_server.Response.PqcPrekeys',
|
||||||
|
'9': 0,
|
||||||
|
'10': 'prekeysPqc'
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'8': [
|
'8': [
|
||||||
{'1': 'Ok'},
|
{'1': 'Ok'},
|
||||||
|
|
@ -1185,5 +1313,8 @@ final $typed_data.Uint8List responseDescriptor = $convert.base64Decode(
|
||||||
'ICb2sSKAoFZXJyb3IYAiABKA4yEC5lcnJvci5FcnJvckNvZGVIAFIFZXJyb3IaMAoGUHJlS2V5'
|
'ICb2sSKAoFZXJyb3IYAiABKA4yEC5lcnJvci5FcnJvckNvZGVIAFIFZXJyb3IaMAoGUHJlS2V5'
|
||||||
'Eg4KAmlkGAEgASgDUgJpZBIWCgZwcmVrZXkYAiABKAxSBnByZWtleRpGCgdQcmVrZXlzEjsKB3'
|
'Eg4KAmlkGAEgASgDUgJpZBIWCgZwcmVrZXkYAiABKAxSBnByZWtleRpGCgdQcmVrZXlzEjsKB3'
|
||||||
'ByZWtleXMYASADKAsyIS5jbGllbnRfdG9fc2VydmVyLlJlc3BvbnNlLlByZUtleVIHcHJla2V5'
|
'ByZWtleXMYASADKAsyIS5jbGllbnRfdG9fc2VydmVyLlJlc3BvbnNlLlByZUtleVIHcHJla2V5'
|
||||||
'cxpgCgJPaxIUCgROb25lGAEgASgISABSBE5vbmUSPgoHcHJla2V5cxgCIAEoCzIiLmNsaWVudF'
|
'cxpTCgpQcWNQcmVrZXlzEkUKB3ByZWtleXMYASADKAsyKy5jbGllbnRfdG9fc2VydmVyLkFwcG'
|
||||||
'90b19zZXJ2ZXIuUmVzcG9uc2UuUHJla2V5c0gAUgdwcmVrZXlzQgQKAk9rQgoKCFJlc3BvbnNl');
|
'xpY2F0aW9uRGF0YS5QcWNQcmVLZXlSB3ByZWtleXMaqgEKAk9rEhQKBE5vbmUYASABKAhIAFIE'
|
||||||
|
'Tm9uZRI+CgdwcmVrZXlzGAIgASgLMiIuY2xpZW50X3RvX3NlcnZlci5SZXNwb25zZS5QcmVrZX'
|
||||||
|
'lzSABSB3ByZWtleXMSSAoLcHJla2V5c19wcWMYAyABKAsyJS5jbGllbnRfdG9fc2VydmVyLlJl'
|
||||||
|
'c3BvbnNlLlBxY1ByZWtleXNIAFIKcHJla2V5c1BxY0IECgJPa0IKCghSZXNwb25zZQ==');
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ enum V0_Kind {
|
||||||
requestNewPreKeys,
|
requestNewPreKeys,
|
||||||
error,
|
error,
|
||||||
newMessages,
|
newMessages,
|
||||||
|
requestNewPqcPreKeys,
|
||||||
notSet
|
notSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,6 +107,7 @@ class V0 extends $pb.GeneratedMessage {
|
||||||
$core.bool? requestNewPreKeys,
|
$core.bool? requestNewPreKeys,
|
||||||
$0.ErrorCode? error,
|
$0.ErrorCode? error,
|
||||||
NewMessages? newMessages,
|
NewMessages? newMessages,
|
||||||
|
$core.bool? requestNewPqcPreKeys,
|
||||||
}) {
|
}) {
|
||||||
final result = create();
|
final result = create();
|
||||||
if (seq != null) result.seq = seq;
|
if (seq != null) result.seq = seq;
|
||||||
|
|
@ -114,6 +116,8 @@ class V0 extends $pb.GeneratedMessage {
|
||||||
if (requestNewPreKeys != null) result.requestNewPreKeys = requestNewPreKeys;
|
if (requestNewPreKeys != null) result.requestNewPreKeys = requestNewPreKeys;
|
||||||
if (error != null) result.error = error;
|
if (error != null) result.error = error;
|
||||||
if (newMessages != null) result.newMessages = newMessages;
|
if (newMessages != null) result.newMessages = newMessages;
|
||||||
|
if (requestNewPqcPreKeys != null)
|
||||||
|
result.requestNewPqcPreKeys = requestNewPqcPreKeys;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,6 +136,7 @@ class V0 extends $pb.GeneratedMessage {
|
||||||
4: V0_Kind.requestNewPreKeys,
|
4: V0_Kind.requestNewPreKeys,
|
||||||
6: V0_Kind.error,
|
6: V0_Kind.error,
|
||||||
7: V0_Kind.newMessages,
|
7: V0_Kind.newMessages,
|
||||||
|
8: V0_Kind.requestNewPqcPreKeys,
|
||||||
0: V0_Kind.notSet
|
0: V0_Kind.notSet
|
||||||
};
|
};
|
||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||||
|
|
@ -139,7 +144,7 @@ class V0 extends $pb.GeneratedMessage {
|
||||||
package:
|
package:
|
||||||
const $pb.PackageName(_omitMessageNames ? '' : 'server_to_client'),
|
const $pb.PackageName(_omitMessageNames ? '' : 'server_to_client'),
|
||||||
createEmptyInstance: create)
|
createEmptyInstance: create)
|
||||||
..oo(0, [2, 3, 4, 6, 7])
|
..oo(0, [2, 3, 4, 6, 7, 8])
|
||||||
..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'seq', $pb.PbFieldType.OU6,
|
..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'seq', $pb.PbFieldType.OU6,
|
||||||
defaultOrMaker: $fixnum.Int64.ZERO)
|
defaultOrMaker: $fixnum.Int64.ZERO)
|
||||||
..aOM<Response>(2, _omitFieldNames ? '' : 'response',
|
..aOM<Response>(2, _omitFieldNames ? '' : 'response',
|
||||||
|
|
@ -152,6 +157,8 @@ class V0 extends $pb.GeneratedMessage {
|
||||||
enumValues: $0.ErrorCode.values)
|
enumValues: $0.ErrorCode.values)
|
||||||
..aOM<NewMessages>(7, _omitFieldNames ? '' : 'newMessages',
|
..aOM<NewMessages>(7, _omitFieldNames ? '' : 'newMessages',
|
||||||
protoName: 'newMessages', subBuilder: NewMessages.create)
|
protoName: 'newMessages', subBuilder: NewMessages.create)
|
||||||
|
..aOB(8, _omitFieldNames ? '' : 'RequestNewPqcPreKeys',
|
||||||
|
protoName: 'RequestNewPqcPreKeys')
|
||||||
..hasRequiredFields = false;
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
|
@ -177,12 +184,14 @@ class V0 extends $pb.GeneratedMessage {
|
||||||
@$pb.TagNumber(4)
|
@$pb.TagNumber(4)
|
||||||
@$pb.TagNumber(6)
|
@$pb.TagNumber(6)
|
||||||
@$pb.TagNumber(7)
|
@$pb.TagNumber(7)
|
||||||
|
@$pb.TagNumber(8)
|
||||||
V0_Kind whichKind() => _V0_KindByTag[$_whichOneof(0)]!;
|
V0_Kind whichKind() => _V0_KindByTag[$_whichOneof(0)]!;
|
||||||
@$pb.TagNumber(2)
|
@$pb.TagNumber(2)
|
||||||
@$pb.TagNumber(3)
|
@$pb.TagNumber(3)
|
||||||
@$pb.TagNumber(4)
|
@$pb.TagNumber(4)
|
||||||
@$pb.TagNumber(6)
|
@$pb.TagNumber(6)
|
||||||
@$pb.TagNumber(7)
|
@$pb.TagNumber(7)
|
||||||
|
@$pb.TagNumber(8)
|
||||||
void clearKind() => $_clearField($_whichOneof(0));
|
void clearKind() => $_clearField($_whichOneof(0));
|
||||||
|
|
||||||
@$pb.TagNumber(1)
|
@$pb.TagNumber(1)
|
||||||
|
|
@ -244,6 +253,15 @@ class V0 extends $pb.GeneratedMessage {
|
||||||
void clearNewMessages() => $_clearField(7);
|
void clearNewMessages() => $_clearField(7);
|
||||||
@$pb.TagNumber(7)
|
@$pb.TagNumber(7)
|
||||||
NewMessages ensureNewMessages() => $_ensure(5);
|
NewMessages ensureNewMessages() => $_ensure(5);
|
||||||
|
|
||||||
|
@$pb.TagNumber(8)
|
||||||
|
$core.bool get requestNewPqcPreKeys => $_getBF(6);
|
||||||
|
@$pb.TagNumber(8)
|
||||||
|
set requestNewPqcPreKeys($core.bool value) => $_setBool(6, value);
|
||||||
|
@$pb.TagNumber(8)
|
||||||
|
$core.bool hasRequestNewPqcPreKeys() => $_has(6);
|
||||||
|
@$pb.TagNumber(8)
|
||||||
|
void clearRequestNewPqcPreKeys() => $_clearField(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
class NewMessage extends $pb.GeneratedMessage {
|
class NewMessage extends $pb.GeneratedMessage {
|
||||||
|
|
@ -830,45 +848,6 @@ class Response_AdditionalAccount extends $pb.GeneratedMessage {
|
||||||
void clearPlanId() => $_clearField(3);
|
void clearPlanId() => $_clearField(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Response_Deprecated extends $pb.GeneratedMessage {
|
|
||||||
factory Response_Deprecated() => create();
|
|
||||||
|
|
||||||
Response_Deprecated._();
|
|
||||||
|
|
||||||
factory Response_Deprecated.fromBuffer($core.List<$core.int> data,
|
|
||||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
|
||||||
create()..mergeFromBuffer(data, registry);
|
|
||||||
factory Response_Deprecated.fromJson($core.String json,
|
|
||||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
|
||||||
create()..mergeFromJson(json, registry);
|
|
||||||
|
|
||||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
|
||||||
_omitMessageNames ? '' : 'Response.Deprecated',
|
|
||||||
package:
|
|
||||||
const $pb.PackageName(_omitMessageNames ? '' : 'server_to_client'),
|
|
||||||
createEmptyInstance: create)
|
|
||||||
..hasRequiredFields = false;
|
|
||||||
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
|
||||||
Response_Deprecated clone() => deepCopy();
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
|
||||||
Response_Deprecated copyWith(void Function(Response_Deprecated) updates) =>
|
|
||||||
super.copyWith((message) => updates(message as Response_Deprecated))
|
|
||||||
as Response_Deprecated;
|
|
||||||
|
|
||||||
@$core.override
|
|
||||||
$pb.BuilderInfo get info_ => _i;
|
|
||||||
|
|
||||||
@$core.pragma('dart2js:noInline')
|
|
||||||
static Response_Deprecated create() => Response_Deprecated._();
|
|
||||||
@$core.override
|
|
||||||
Response_Deprecated createEmptyInstance() => create();
|
|
||||||
@$core.pragma('dart2js:noInline')
|
|
||||||
static Response_Deprecated getDefault() => _defaultInstance ??=
|
|
||||||
$pb.GeneratedMessage.$_defaultFor<Response_Deprecated>(create);
|
|
||||||
static Response_Deprecated? _defaultInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Response_Transaction extends $pb.GeneratedMessage {
|
class Response_Transaction extends $pb.GeneratedMessage {
|
||||||
factory Response_Transaction() => create();
|
factory Response_Transaction() => create();
|
||||||
|
|
||||||
|
|
@ -1196,6 +1175,252 @@ class Response_SignedPreKey extends $pb.GeneratedMessage {
|
||||||
void clearSignedPrekeySignature() => $_clearField(3);
|
void clearSignedPrekeySignature() => $_clearField(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Response_PqcPreKey extends $pb.GeneratedMessage {
|
||||||
|
factory Response_PqcPreKey({
|
||||||
|
$fixnum.Int64? eccPreKeyId,
|
||||||
|
$core.List<$core.int>? eccPreKey,
|
||||||
|
$fixnum.Int64? kyberPreKeyId,
|
||||||
|
$core.List<$core.int>? kyberPreKey,
|
||||||
|
$core.List<$core.int>? kyberPreKeySignature,
|
||||||
|
}) {
|
||||||
|
final result = create();
|
||||||
|
if (eccPreKeyId != null) result.eccPreKeyId = eccPreKeyId;
|
||||||
|
if (eccPreKey != null) result.eccPreKey = eccPreKey;
|
||||||
|
if (kyberPreKeyId != null) result.kyberPreKeyId = kyberPreKeyId;
|
||||||
|
if (kyberPreKey != null) result.kyberPreKey = kyberPreKey;
|
||||||
|
if (kyberPreKeySignature != null)
|
||||||
|
result.kyberPreKeySignature = kyberPreKeySignature;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Response_PqcPreKey._();
|
||||||
|
|
||||||
|
factory Response_PqcPreKey.fromBuffer($core.List<$core.int> data,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromBuffer(data, registry);
|
||||||
|
factory Response_PqcPreKey.fromJson($core.String json,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromJson(json, registry);
|
||||||
|
|
||||||
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||||
|
_omitMessageNames ? '' : 'Response.PqcPreKey',
|
||||||
|
package:
|
||||||
|
const $pb.PackageName(_omitMessageNames ? '' : 'server_to_client'),
|
||||||
|
createEmptyInstance: create)
|
||||||
|
..aInt64(1, _omitFieldNames ? '' : 'eccPreKeyId')
|
||||||
|
..a<$core.List<$core.int>>(
|
||||||
|
2, _omitFieldNames ? '' : 'eccPreKey', $pb.PbFieldType.OY)
|
||||||
|
..aInt64(3, _omitFieldNames ? '' : 'kyberPreKeyId')
|
||||||
|
..a<$core.List<$core.int>>(
|
||||||
|
4, _omitFieldNames ? '' : 'kyberPreKey', $pb.PbFieldType.OY)
|
||||||
|
..a<$core.List<$core.int>>(
|
||||||
|
5, _omitFieldNames ? '' : 'kyberPreKeySignature', $pb.PbFieldType.OY)
|
||||||
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
Response_PqcPreKey clone() => deepCopy();
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
Response_PqcPreKey copyWith(void Function(Response_PqcPreKey) updates) =>
|
||||||
|
super.copyWith((message) => updates(message as Response_PqcPreKey))
|
||||||
|
as Response_PqcPreKey;
|
||||||
|
|
||||||
|
@$core.override
|
||||||
|
$pb.BuilderInfo get info_ => _i;
|
||||||
|
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static Response_PqcPreKey create() => Response_PqcPreKey._();
|
||||||
|
@$core.override
|
||||||
|
Response_PqcPreKey createEmptyInstance() => create();
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static Response_PqcPreKey getDefault() => _defaultInstance ??=
|
||||||
|
$pb.GeneratedMessage.$_defaultFor<Response_PqcPreKey>(create);
|
||||||
|
static Response_PqcPreKey? _defaultInstance;
|
||||||
|
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$fixnum.Int64 get eccPreKeyId => $_getI64(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
set eccPreKeyId($fixnum.Int64 value) => $_setInt64(0, value);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$core.bool hasEccPreKeyId() => $_has(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
void clearEccPreKeyId() => $_clearField(1);
|
||||||
|
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$core.List<$core.int> get eccPreKey => $_getN(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
set eccPreKey($core.List<$core.int> value) => $_setBytes(1, value);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$core.bool hasEccPreKey() => $_has(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
void clearEccPreKey() => $_clearField(2);
|
||||||
|
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$fixnum.Int64 get kyberPreKeyId => $_getI64(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
set kyberPreKeyId($fixnum.Int64 value) => $_setInt64(2, value);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$core.bool hasKyberPreKeyId() => $_has(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
void clearKyberPreKeyId() => $_clearField(3);
|
||||||
|
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$core.List<$core.int> get kyberPreKey => $_getN(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
set kyberPreKey($core.List<$core.int> value) => $_setBytes(3, value);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$core.bool hasKyberPreKey() => $_has(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
void clearKyberPreKey() => $_clearField(4);
|
||||||
|
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
$core.List<$core.int> get kyberPreKeySignature => $_getN(4);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
set kyberPreKeySignature($core.List<$core.int> value) => $_setBytes(4, value);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
$core.bool hasKyberPreKeySignature() => $_has(4);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
void clearKyberPreKeySignature() => $_clearField(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Response_PqcBundle extends $pb.GeneratedMessage {
|
||||||
|
factory Response_PqcBundle({
|
||||||
|
$fixnum.Int64? eccSignedPrekeyId,
|
||||||
|
$core.List<$core.int>? eccSignedPrekey,
|
||||||
|
$core.List<$core.int>? eccSignedPrekeySignature,
|
||||||
|
$fixnum.Int64? kyberSignedPrekeyId,
|
||||||
|
$core.List<$core.int>? kyberSignedPrekey,
|
||||||
|
$core.List<$core.int>? kyberSignedPrekeySignature,
|
||||||
|
Response_PqcPreKey? prekey,
|
||||||
|
}) {
|
||||||
|
final result = create();
|
||||||
|
if (eccSignedPrekeyId != null) result.eccSignedPrekeyId = eccSignedPrekeyId;
|
||||||
|
if (eccSignedPrekey != null) result.eccSignedPrekey = eccSignedPrekey;
|
||||||
|
if (eccSignedPrekeySignature != null)
|
||||||
|
result.eccSignedPrekeySignature = eccSignedPrekeySignature;
|
||||||
|
if (kyberSignedPrekeyId != null)
|
||||||
|
result.kyberSignedPrekeyId = kyberSignedPrekeyId;
|
||||||
|
if (kyberSignedPrekey != null) result.kyberSignedPrekey = kyberSignedPrekey;
|
||||||
|
if (kyberSignedPrekeySignature != null)
|
||||||
|
result.kyberSignedPrekeySignature = kyberSignedPrekeySignature;
|
||||||
|
if (prekey != null) result.prekey = prekey;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Response_PqcBundle._();
|
||||||
|
|
||||||
|
factory Response_PqcBundle.fromBuffer($core.List<$core.int> data,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromBuffer(data, registry);
|
||||||
|
factory Response_PqcBundle.fromJson($core.String json,
|
||||||
|
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||||
|
create()..mergeFromJson(json, registry);
|
||||||
|
|
||||||
|
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||||
|
_omitMessageNames ? '' : 'Response.PqcBundle',
|
||||||
|
package:
|
||||||
|
const $pb.PackageName(_omitMessageNames ? '' : 'server_to_client'),
|
||||||
|
createEmptyInstance: create)
|
||||||
|
..aInt64(1, _omitFieldNames ? '' : 'eccSignedPrekeyId')
|
||||||
|
..a<$core.List<$core.int>>(
|
||||||
|
2, _omitFieldNames ? '' : 'eccSignedPrekey', $pb.PbFieldType.OY)
|
||||||
|
..a<$core.List<$core.int>>(3,
|
||||||
|
_omitFieldNames ? '' : 'eccSignedPrekeySignature', $pb.PbFieldType.OY)
|
||||||
|
..aInt64(4, _omitFieldNames ? '' : 'kyberSignedPrekeyId')
|
||||||
|
..a<$core.List<$core.int>>(
|
||||||
|
5, _omitFieldNames ? '' : 'kyberSignedPrekey', $pb.PbFieldType.OY)
|
||||||
|
..a<$core.List<$core.int>>(6,
|
||||||
|
_omitFieldNames ? '' : 'kyberSignedPrekeySignature', $pb.PbFieldType.OY)
|
||||||
|
..aOM<Response_PqcPreKey>(7, _omitFieldNames ? '' : 'prekey',
|
||||||
|
subBuilder: Response_PqcPreKey.create)
|
||||||
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
Response_PqcBundle clone() => deepCopy();
|
||||||
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
Response_PqcBundle copyWith(void Function(Response_PqcBundle) updates) =>
|
||||||
|
super.copyWith((message) => updates(message as Response_PqcBundle))
|
||||||
|
as Response_PqcBundle;
|
||||||
|
|
||||||
|
@$core.override
|
||||||
|
$pb.BuilderInfo get info_ => _i;
|
||||||
|
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static Response_PqcBundle create() => Response_PqcBundle._();
|
||||||
|
@$core.override
|
||||||
|
Response_PqcBundle createEmptyInstance() => create();
|
||||||
|
@$core.pragma('dart2js:noInline')
|
||||||
|
static Response_PqcBundle getDefault() => _defaultInstance ??=
|
||||||
|
$pb.GeneratedMessage.$_defaultFor<Response_PqcBundle>(create);
|
||||||
|
static Response_PqcBundle? _defaultInstance;
|
||||||
|
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$fixnum.Int64 get eccSignedPrekeyId => $_getI64(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
set eccSignedPrekeyId($fixnum.Int64 value) => $_setInt64(0, value);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
$core.bool hasEccSignedPrekeyId() => $_has(0);
|
||||||
|
@$pb.TagNumber(1)
|
||||||
|
void clearEccSignedPrekeyId() => $_clearField(1);
|
||||||
|
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$core.List<$core.int> get eccSignedPrekey => $_getN(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
set eccSignedPrekey($core.List<$core.int> value) => $_setBytes(1, value);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
$core.bool hasEccSignedPrekey() => $_has(1);
|
||||||
|
@$pb.TagNumber(2)
|
||||||
|
void clearEccSignedPrekey() => $_clearField(2);
|
||||||
|
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$core.List<$core.int> get eccSignedPrekeySignature => $_getN(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
set eccSignedPrekeySignature($core.List<$core.int> value) =>
|
||||||
|
$_setBytes(2, value);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
$core.bool hasEccSignedPrekeySignature() => $_has(2);
|
||||||
|
@$pb.TagNumber(3)
|
||||||
|
void clearEccSignedPrekeySignature() => $_clearField(3);
|
||||||
|
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$fixnum.Int64 get kyberSignedPrekeyId => $_getI64(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
set kyberSignedPrekeyId($fixnum.Int64 value) => $_setInt64(3, value);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
$core.bool hasKyberSignedPrekeyId() => $_has(3);
|
||||||
|
@$pb.TagNumber(4)
|
||||||
|
void clearKyberSignedPrekeyId() => $_clearField(4);
|
||||||
|
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
$core.List<$core.int> get kyberSignedPrekey => $_getN(4);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
set kyberSignedPrekey($core.List<$core.int> value) => $_setBytes(4, value);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
$core.bool hasKyberSignedPrekey() => $_has(4);
|
||||||
|
@$pb.TagNumber(5)
|
||||||
|
void clearKyberSignedPrekey() => $_clearField(5);
|
||||||
|
|
||||||
|
@$pb.TagNumber(6)
|
||||||
|
$core.List<$core.int> get kyberSignedPrekeySignature => $_getN(5);
|
||||||
|
@$pb.TagNumber(6)
|
||||||
|
set kyberSignedPrekeySignature($core.List<$core.int> value) =>
|
||||||
|
$_setBytes(5, value);
|
||||||
|
@$pb.TagNumber(6)
|
||||||
|
$core.bool hasKyberSignedPrekeySignature() => $_has(5);
|
||||||
|
@$pb.TagNumber(6)
|
||||||
|
void clearKyberSignedPrekeySignature() => $_clearField(6);
|
||||||
|
|
||||||
|
@$pb.TagNumber(7)
|
||||||
|
Response_PqcPreKey get prekey => $_getN(6);
|
||||||
|
@$pb.TagNumber(7)
|
||||||
|
set prekey(Response_PqcPreKey value) => $_setField(7, value);
|
||||||
|
@$pb.TagNumber(7)
|
||||||
|
$core.bool hasPrekey() => $_has(6);
|
||||||
|
@$pb.TagNumber(7)
|
||||||
|
void clearPrekey() => $_clearField(7);
|
||||||
|
@$pb.TagNumber(7)
|
||||||
|
Response_PqcPreKey ensurePrekey() => $_ensure(6);
|
||||||
|
}
|
||||||
|
|
||||||
class Response_UserData extends $pb.GeneratedMessage {
|
class Response_UserData extends $pb.GeneratedMessage {
|
||||||
factory Response_UserData({
|
factory Response_UserData({
|
||||||
$fixnum.Int64? userId,
|
$fixnum.Int64? userId,
|
||||||
|
|
@ -1206,6 +1431,7 @@ class Response_UserData extends $pb.GeneratedMessage {
|
||||||
$fixnum.Int64? signedPrekeyId,
|
$fixnum.Int64? signedPrekeyId,
|
||||||
$core.List<$core.int>? username,
|
$core.List<$core.int>? username,
|
||||||
$fixnum.Int64? registrationId,
|
$fixnum.Int64? registrationId,
|
||||||
|
Response_PqcBundle? pqcBundle,
|
||||||
}) {
|
}) {
|
||||||
final result = create();
|
final result = create();
|
||||||
if (userId != null) result.userId = userId;
|
if (userId != null) result.userId = userId;
|
||||||
|
|
@ -1217,6 +1443,7 @@ class Response_UserData extends $pb.GeneratedMessage {
|
||||||
if (signedPrekeyId != null) result.signedPrekeyId = signedPrekeyId;
|
if (signedPrekeyId != null) result.signedPrekeyId = signedPrekeyId;
|
||||||
if (username != null) result.username = username;
|
if (username != null) result.username = username;
|
||||||
if (registrationId != null) result.registrationId = registrationId;
|
if (registrationId != null) result.registrationId = registrationId;
|
||||||
|
if (pqcBundle != null) result.pqcBundle = pqcBundle;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1247,6 +1474,8 @@ class Response_UserData extends $pb.GeneratedMessage {
|
||||||
..a<$core.List<$core.int>>(
|
..a<$core.List<$core.int>>(
|
||||||
7, _omitFieldNames ? '' : 'username', $pb.PbFieldType.OY)
|
7, _omitFieldNames ? '' : 'username', $pb.PbFieldType.OY)
|
||||||
..aInt64(8, _omitFieldNames ? '' : 'registrationId')
|
..aInt64(8, _omitFieldNames ? '' : 'registrationId')
|
||||||
|
..aOM<Response_PqcBundle>(9, _omitFieldNames ? '' : 'pqcBundle',
|
||||||
|
subBuilder: Response_PqcBundle.create)
|
||||||
..hasRequiredFields = false;
|
..hasRequiredFields = false;
|
||||||
|
|
||||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||||
|
|
@ -1334,6 +1563,17 @@ class Response_UserData extends $pb.GeneratedMessage {
|
||||||
$core.bool hasRegistrationId() => $_has(7);
|
$core.bool hasRegistrationId() => $_has(7);
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
void clearRegistrationId() => $_clearField(8);
|
void clearRegistrationId() => $_clearField(8);
|
||||||
|
|
||||||
|
@$pb.TagNumber(9)
|
||||||
|
Response_PqcBundle get pqcBundle => $_getN(8);
|
||||||
|
@$pb.TagNumber(9)
|
||||||
|
set pqcBundle(Response_PqcBundle value) => $_setField(9, value);
|
||||||
|
@$pb.TagNumber(9)
|
||||||
|
$core.bool hasPqcBundle() => $_has(8);
|
||||||
|
@$pb.TagNumber(9)
|
||||||
|
void clearPqcBundle() => $_clearField(9);
|
||||||
|
@$pb.TagNumber(9)
|
||||||
|
Response_PqcBundle ensurePqcBundle() => $_ensure(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Response_UploadToken extends $pb.GeneratedMessage {
|
class Response_UploadToken extends $pb.GeneratedMessage {
|
||||||
|
|
@ -2074,11 +2314,9 @@ enum Response_Ok_Ok {
|
||||||
uploadtoken,
|
uploadtoken,
|
||||||
userdata,
|
userdata,
|
||||||
authtoken,
|
authtoken,
|
||||||
deprecated7,
|
|
||||||
authenticated,
|
authenticated,
|
||||||
plans,
|
plans,
|
||||||
planballance,
|
planballance,
|
||||||
deprecated11,
|
|
||||||
addaccountsinvites,
|
addaccountsinvites,
|
||||||
downloadtokens,
|
downloadtokens,
|
||||||
signedprekey,
|
signedprekey,
|
||||||
|
|
@ -2100,11 +2338,9 @@ class Response_Ok extends $pb.GeneratedMessage {
|
||||||
Response_UploadToken? uploadtoken,
|
Response_UploadToken? uploadtoken,
|
||||||
Response_UserData? userdata,
|
Response_UserData? userdata,
|
||||||
$core.List<$core.int>? authtoken,
|
$core.List<$core.int>? authtoken,
|
||||||
Response_Deprecated? deprecated7,
|
|
||||||
Response_Authenticated? authenticated,
|
Response_Authenticated? authenticated,
|
||||||
Response_Plans? plans,
|
Response_Plans? plans,
|
||||||
Response_PlanBallance? planballance,
|
Response_PlanBallance? planballance,
|
||||||
Response_Deprecated? deprecated11,
|
|
||||||
Response_AddAccountsInvites? addaccountsinvites,
|
Response_AddAccountsInvites? addaccountsinvites,
|
||||||
Response_DownloadTokens? downloadtokens,
|
Response_DownloadTokens? downloadtokens,
|
||||||
Response_SignedPreKey? signedprekey,
|
Response_SignedPreKey? signedprekey,
|
||||||
|
|
@ -2123,11 +2359,9 @@ class Response_Ok extends $pb.GeneratedMessage {
|
||||||
if (uploadtoken != null) result.uploadtoken = uploadtoken;
|
if (uploadtoken != null) result.uploadtoken = uploadtoken;
|
||||||
if (userdata != null) result.userdata = userdata;
|
if (userdata != null) result.userdata = userdata;
|
||||||
if (authtoken != null) result.authtoken = authtoken;
|
if (authtoken != null) result.authtoken = authtoken;
|
||||||
if (deprecated7 != null) result.deprecated7 = deprecated7;
|
|
||||||
if (authenticated != null) result.authenticated = authenticated;
|
if (authenticated != null) result.authenticated = authenticated;
|
||||||
if (plans != null) result.plans = plans;
|
if (plans != null) result.plans = plans;
|
||||||
if (planballance != null) result.planballance = planballance;
|
if (planballance != null) result.planballance = planballance;
|
||||||
if (deprecated11 != null) result.deprecated11 = deprecated11;
|
|
||||||
if (addaccountsinvites != null)
|
if (addaccountsinvites != null)
|
||||||
result.addaccountsinvites = addaccountsinvites;
|
result.addaccountsinvites = addaccountsinvites;
|
||||||
if (downloadtokens != null) result.downloadtokens = downloadtokens;
|
if (downloadtokens != null) result.downloadtokens = downloadtokens;
|
||||||
|
|
@ -2162,11 +2396,9 @@ class Response_Ok extends $pb.GeneratedMessage {
|
||||||
4: Response_Ok_Ok.uploadtoken,
|
4: Response_Ok_Ok.uploadtoken,
|
||||||
5: Response_Ok_Ok.userdata,
|
5: Response_Ok_Ok.userdata,
|
||||||
6: Response_Ok_Ok.authtoken,
|
6: Response_Ok_Ok.authtoken,
|
||||||
7: Response_Ok_Ok.deprecated7,
|
|
||||||
8: Response_Ok_Ok.authenticated,
|
8: Response_Ok_Ok.authenticated,
|
||||||
9: Response_Ok_Ok.plans,
|
9: Response_Ok_Ok.plans,
|
||||||
10: Response_Ok_Ok.planballance,
|
10: Response_Ok_Ok.planballance,
|
||||||
11: Response_Ok_Ok.deprecated11,
|
|
||||||
12: Response_Ok_Ok.addaccountsinvites,
|
12: Response_Ok_Ok.addaccountsinvites,
|
||||||
13: Response_Ok_Ok.downloadtokens,
|
13: Response_Ok_Ok.downloadtokens,
|
||||||
14: Response_Ok_Ok.signedprekey,
|
14: Response_Ok_Ok.signedprekey,
|
||||||
|
|
@ -2184,29 +2416,8 @@ class Response_Ok extends $pb.GeneratedMessage {
|
||||||
package:
|
package:
|
||||||
const $pb.PackageName(_omitMessageNames ? '' : 'server_to_client'),
|
const $pb.PackageName(_omitMessageNames ? '' : 'server_to_client'),
|
||||||
createEmptyInstance: create)
|
createEmptyInstance: create)
|
||||||
..oo(0, [
|
..oo(
|
||||||
1,
|
0, [1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21])
|
||||||
2,
|
|
||||||
3,
|
|
||||||
4,
|
|
||||||
5,
|
|
||||||
6,
|
|
||||||
7,
|
|
||||||
8,
|
|
||||||
9,
|
|
||||||
10,
|
|
||||||
11,
|
|
||||||
12,
|
|
||||||
13,
|
|
||||||
14,
|
|
||||||
15,
|
|
||||||
16,
|
|
||||||
17,
|
|
||||||
18,
|
|
||||||
19,
|
|
||||||
20,
|
|
||||||
21
|
|
||||||
])
|
|
||||||
..aOB(1, _omitFieldNames ? '' : 'None', protoName: 'None')
|
..aOB(1, _omitFieldNames ? '' : 'None', protoName: 'None')
|
||||||
..aInt64(2, _omitFieldNames ? '' : 'userid')
|
..aInt64(2, _omitFieldNames ? '' : 'userid')
|
||||||
..a<$core.List<$core.int>>(
|
..a<$core.List<$core.int>>(
|
||||||
|
|
@ -2217,16 +2428,12 @@ class Response_Ok extends $pb.GeneratedMessage {
|
||||||
subBuilder: Response_UserData.create)
|
subBuilder: Response_UserData.create)
|
||||||
..a<$core.List<$core.int>>(
|
..a<$core.List<$core.int>>(
|
||||||
6, _omitFieldNames ? '' : 'authtoken', $pb.PbFieldType.OY)
|
6, _omitFieldNames ? '' : 'authtoken', $pb.PbFieldType.OY)
|
||||||
..aOM<Response_Deprecated>(7, _omitFieldNames ? '' : 'deprecated7',
|
|
||||||
protoName: 'deprecated_7', subBuilder: Response_Deprecated.create)
|
|
||||||
..aOM<Response_Authenticated>(8, _omitFieldNames ? '' : 'authenticated',
|
..aOM<Response_Authenticated>(8, _omitFieldNames ? '' : 'authenticated',
|
||||||
subBuilder: Response_Authenticated.create)
|
subBuilder: Response_Authenticated.create)
|
||||||
..aOM<Response_Plans>(9, _omitFieldNames ? '' : 'plans',
|
..aOM<Response_Plans>(9, _omitFieldNames ? '' : 'plans',
|
||||||
subBuilder: Response_Plans.create)
|
subBuilder: Response_Plans.create)
|
||||||
..aOM<Response_PlanBallance>(10, _omitFieldNames ? '' : 'planballance',
|
..aOM<Response_PlanBallance>(10, _omitFieldNames ? '' : 'planballance',
|
||||||
subBuilder: Response_PlanBallance.create)
|
subBuilder: Response_PlanBallance.create)
|
||||||
..aOM<Response_Deprecated>(11, _omitFieldNames ? '' : 'deprecated11',
|
|
||||||
protoName: 'deprecated_11', subBuilder: Response_Deprecated.create)
|
|
||||||
..aOM<Response_AddAccountsInvites>(
|
..aOM<Response_AddAccountsInvites>(
|
||||||
12, _omitFieldNames ? '' : 'addaccountsinvites',
|
12, _omitFieldNames ? '' : 'addaccountsinvites',
|
||||||
subBuilder: Response_AddAccountsInvites.create)
|
subBuilder: Response_AddAccountsInvites.create)
|
||||||
|
|
@ -2279,11 +2486,9 @@ class Response_Ok extends $pb.GeneratedMessage {
|
||||||
@$pb.TagNumber(4)
|
@$pb.TagNumber(4)
|
||||||
@$pb.TagNumber(5)
|
@$pb.TagNumber(5)
|
||||||
@$pb.TagNumber(6)
|
@$pb.TagNumber(6)
|
||||||
@$pb.TagNumber(7)
|
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
@$pb.TagNumber(9)
|
@$pb.TagNumber(9)
|
||||||
@$pb.TagNumber(10)
|
@$pb.TagNumber(10)
|
||||||
@$pb.TagNumber(11)
|
|
||||||
@$pb.TagNumber(12)
|
@$pb.TagNumber(12)
|
||||||
@$pb.TagNumber(13)
|
@$pb.TagNumber(13)
|
||||||
@$pb.TagNumber(14)
|
@$pb.TagNumber(14)
|
||||||
|
|
@ -2301,11 +2506,9 @@ class Response_Ok extends $pb.GeneratedMessage {
|
||||||
@$pb.TagNumber(4)
|
@$pb.TagNumber(4)
|
||||||
@$pb.TagNumber(5)
|
@$pb.TagNumber(5)
|
||||||
@$pb.TagNumber(6)
|
@$pb.TagNumber(6)
|
||||||
@$pb.TagNumber(7)
|
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
@$pb.TagNumber(9)
|
@$pb.TagNumber(9)
|
||||||
@$pb.TagNumber(10)
|
@$pb.TagNumber(10)
|
||||||
@$pb.TagNumber(11)
|
|
||||||
@$pb.TagNumber(12)
|
@$pb.TagNumber(12)
|
||||||
@$pb.TagNumber(13)
|
@$pb.TagNumber(13)
|
||||||
@$pb.TagNumber(14)
|
@$pb.TagNumber(14)
|
||||||
|
|
@ -2376,175 +2579,153 @@ class Response_Ok extends $pb.GeneratedMessage {
|
||||||
@$pb.TagNumber(6)
|
@$pb.TagNumber(6)
|
||||||
void clearAuthtoken() => $_clearField(6);
|
void clearAuthtoken() => $_clearField(6);
|
||||||
|
|
||||||
@$pb.TagNumber(7)
|
|
||||||
Response_Deprecated get deprecated7 => $_getN(6);
|
|
||||||
@$pb.TagNumber(7)
|
|
||||||
set deprecated7(Response_Deprecated value) => $_setField(7, value);
|
|
||||||
@$pb.TagNumber(7)
|
|
||||||
$core.bool hasDeprecated7() => $_has(6);
|
|
||||||
@$pb.TagNumber(7)
|
|
||||||
void clearDeprecated7() => $_clearField(7);
|
|
||||||
@$pb.TagNumber(7)
|
|
||||||
Response_Deprecated ensureDeprecated7() => $_ensure(6);
|
|
||||||
|
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
Response_Authenticated get authenticated => $_getN(7);
|
Response_Authenticated get authenticated => $_getN(6);
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
set authenticated(Response_Authenticated value) => $_setField(8, value);
|
set authenticated(Response_Authenticated value) => $_setField(8, value);
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
$core.bool hasAuthenticated() => $_has(7);
|
$core.bool hasAuthenticated() => $_has(6);
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
void clearAuthenticated() => $_clearField(8);
|
void clearAuthenticated() => $_clearField(8);
|
||||||
@$pb.TagNumber(8)
|
@$pb.TagNumber(8)
|
||||||
Response_Authenticated ensureAuthenticated() => $_ensure(7);
|
Response_Authenticated ensureAuthenticated() => $_ensure(6);
|
||||||
|
|
||||||
@$pb.TagNumber(9)
|
@$pb.TagNumber(9)
|
||||||
Response_Plans get plans => $_getN(8);
|
Response_Plans get plans => $_getN(7);
|
||||||
@$pb.TagNumber(9)
|
@$pb.TagNumber(9)
|
||||||
set plans(Response_Plans value) => $_setField(9, value);
|
set plans(Response_Plans value) => $_setField(9, value);
|
||||||
@$pb.TagNumber(9)
|
@$pb.TagNumber(9)
|
||||||
$core.bool hasPlans() => $_has(8);
|
$core.bool hasPlans() => $_has(7);
|
||||||
@$pb.TagNumber(9)
|
@$pb.TagNumber(9)
|
||||||
void clearPlans() => $_clearField(9);
|
void clearPlans() => $_clearField(9);
|
||||||
@$pb.TagNumber(9)
|
@$pb.TagNumber(9)
|
||||||
Response_Plans ensurePlans() => $_ensure(8);
|
Response_Plans ensurePlans() => $_ensure(7);
|
||||||
|
|
||||||
@$pb.TagNumber(10)
|
@$pb.TagNumber(10)
|
||||||
Response_PlanBallance get planballance => $_getN(9);
|
Response_PlanBallance get planballance => $_getN(8);
|
||||||
@$pb.TagNumber(10)
|
@$pb.TagNumber(10)
|
||||||
set planballance(Response_PlanBallance value) => $_setField(10, value);
|
set planballance(Response_PlanBallance value) => $_setField(10, value);
|
||||||
@$pb.TagNumber(10)
|
@$pb.TagNumber(10)
|
||||||
$core.bool hasPlanballance() => $_has(9);
|
$core.bool hasPlanballance() => $_has(8);
|
||||||
@$pb.TagNumber(10)
|
@$pb.TagNumber(10)
|
||||||
void clearPlanballance() => $_clearField(10);
|
void clearPlanballance() => $_clearField(10);
|
||||||
@$pb.TagNumber(10)
|
@$pb.TagNumber(10)
|
||||||
Response_PlanBallance ensurePlanballance() => $_ensure(9);
|
Response_PlanBallance ensurePlanballance() => $_ensure(8);
|
||||||
|
|
||||||
@$pb.TagNumber(11)
|
|
||||||
Response_Deprecated get deprecated11 => $_getN(10);
|
|
||||||
@$pb.TagNumber(11)
|
|
||||||
set deprecated11(Response_Deprecated value) => $_setField(11, value);
|
|
||||||
@$pb.TagNumber(11)
|
|
||||||
$core.bool hasDeprecated11() => $_has(10);
|
|
||||||
@$pb.TagNumber(11)
|
|
||||||
void clearDeprecated11() => $_clearField(11);
|
|
||||||
@$pb.TagNumber(11)
|
|
||||||
Response_Deprecated ensureDeprecated11() => $_ensure(10);
|
|
||||||
|
|
||||||
@$pb.TagNumber(12)
|
@$pb.TagNumber(12)
|
||||||
Response_AddAccountsInvites get addaccountsinvites => $_getN(11);
|
Response_AddAccountsInvites get addaccountsinvites => $_getN(9);
|
||||||
@$pb.TagNumber(12)
|
@$pb.TagNumber(12)
|
||||||
set addaccountsinvites(Response_AddAccountsInvites value) =>
|
set addaccountsinvites(Response_AddAccountsInvites value) =>
|
||||||
$_setField(12, value);
|
$_setField(12, value);
|
||||||
@$pb.TagNumber(12)
|
@$pb.TagNumber(12)
|
||||||
$core.bool hasAddaccountsinvites() => $_has(11);
|
$core.bool hasAddaccountsinvites() => $_has(9);
|
||||||
@$pb.TagNumber(12)
|
@$pb.TagNumber(12)
|
||||||
void clearAddaccountsinvites() => $_clearField(12);
|
void clearAddaccountsinvites() => $_clearField(12);
|
||||||
@$pb.TagNumber(12)
|
@$pb.TagNumber(12)
|
||||||
Response_AddAccountsInvites ensureAddaccountsinvites() => $_ensure(11);
|
Response_AddAccountsInvites ensureAddaccountsinvites() => $_ensure(9);
|
||||||
|
|
||||||
@$pb.TagNumber(13)
|
@$pb.TagNumber(13)
|
||||||
Response_DownloadTokens get downloadtokens => $_getN(12);
|
Response_DownloadTokens get downloadtokens => $_getN(10);
|
||||||
@$pb.TagNumber(13)
|
@$pb.TagNumber(13)
|
||||||
set downloadtokens(Response_DownloadTokens value) => $_setField(13, value);
|
set downloadtokens(Response_DownloadTokens value) => $_setField(13, value);
|
||||||
@$pb.TagNumber(13)
|
@$pb.TagNumber(13)
|
||||||
$core.bool hasDownloadtokens() => $_has(12);
|
$core.bool hasDownloadtokens() => $_has(10);
|
||||||
@$pb.TagNumber(13)
|
@$pb.TagNumber(13)
|
||||||
void clearDownloadtokens() => $_clearField(13);
|
void clearDownloadtokens() => $_clearField(13);
|
||||||
@$pb.TagNumber(13)
|
@$pb.TagNumber(13)
|
||||||
Response_DownloadTokens ensureDownloadtokens() => $_ensure(12);
|
Response_DownloadTokens ensureDownloadtokens() => $_ensure(10);
|
||||||
|
|
||||||
@$pb.TagNumber(14)
|
@$pb.TagNumber(14)
|
||||||
Response_SignedPreKey get signedprekey => $_getN(13);
|
Response_SignedPreKey get signedprekey => $_getN(11);
|
||||||
@$pb.TagNumber(14)
|
@$pb.TagNumber(14)
|
||||||
set signedprekey(Response_SignedPreKey value) => $_setField(14, value);
|
set signedprekey(Response_SignedPreKey value) => $_setField(14, value);
|
||||||
@$pb.TagNumber(14)
|
@$pb.TagNumber(14)
|
||||||
$core.bool hasSignedprekey() => $_has(13);
|
$core.bool hasSignedprekey() => $_has(11);
|
||||||
@$pb.TagNumber(14)
|
@$pb.TagNumber(14)
|
||||||
void clearSignedprekey() => $_clearField(14);
|
void clearSignedprekey() => $_clearField(14);
|
||||||
@$pb.TagNumber(14)
|
@$pb.TagNumber(14)
|
||||||
Response_SignedPreKey ensureSignedprekey() => $_ensure(13);
|
Response_SignedPreKey ensureSignedprekey() => $_ensure(11);
|
||||||
|
|
||||||
@$pb.TagNumber(15)
|
@$pb.TagNumber(15)
|
||||||
Response_ProofOfWork get proofOfWork => $_getN(14);
|
Response_ProofOfWork get proofOfWork => $_getN(12);
|
||||||
@$pb.TagNumber(15)
|
@$pb.TagNumber(15)
|
||||||
set proofOfWork(Response_ProofOfWork value) => $_setField(15, value);
|
set proofOfWork(Response_ProofOfWork value) => $_setField(15, value);
|
||||||
@$pb.TagNumber(15)
|
@$pb.TagNumber(15)
|
||||||
$core.bool hasProofOfWork() => $_has(14);
|
$core.bool hasProofOfWork() => $_has(12);
|
||||||
@$pb.TagNumber(15)
|
@$pb.TagNumber(15)
|
||||||
void clearProofOfWork() => $_clearField(15);
|
void clearProofOfWork() => $_clearField(15);
|
||||||
@$pb.TagNumber(15)
|
@$pb.TagNumber(15)
|
||||||
Response_ProofOfWork ensureProofOfWork() => $_ensure(14);
|
Response_ProofOfWork ensureProofOfWork() => $_ensure(12);
|
||||||
|
|
||||||
@$pb.TagNumber(16)
|
@$pb.TagNumber(16)
|
||||||
$core.List<$core.int> get passwordlessRecoveryServerKey => $_getN(15);
|
$core.List<$core.int> get passwordlessRecoveryServerKey => $_getN(13);
|
||||||
@$pb.TagNumber(16)
|
@$pb.TagNumber(16)
|
||||||
set passwordlessRecoveryServerKey($core.List<$core.int> value) =>
|
set passwordlessRecoveryServerKey($core.List<$core.int> value) =>
|
||||||
$_setBytes(15, value);
|
$_setBytes(13, value);
|
||||||
@$pb.TagNumber(16)
|
@$pb.TagNumber(16)
|
||||||
$core.bool hasPasswordlessRecoveryServerKey() => $_has(15);
|
$core.bool hasPasswordlessRecoveryServerKey() => $_has(13);
|
||||||
@$pb.TagNumber(16)
|
@$pb.TagNumber(16)
|
||||||
void clearPasswordlessRecoveryServerKey() => $_clearField(16);
|
void clearPasswordlessRecoveryServerKey() => $_clearField(16);
|
||||||
|
|
||||||
@$pb.TagNumber(17)
|
@$pb.TagNumber(17)
|
||||||
Response_PasswordlessNotificationMessages
|
Response_PasswordlessNotificationMessages
|
||||||
get passwordlessNotificationMessages => $_getN(16);
|
get passwordlessNotificationMessages => $_getN(14);
|
||||||
@$pb.TagNumber(17)
|
@$pb.TagNumber(17)
|
||||||
set passwordlessNotificationMessages(
|
set passwordlessNotificationMessages(
|
||||||
Response_PasswordlessNotificationMessages value) =>
|
Response_PasswordlessNotificationMessages value) =>
|
||||||
$_setField(17, value);
|
$_setField(17, value);
|
||||||
@$pb.TagNumber(17)
|
@$pb.TagNumber(17)
|
||||||
$core.bool hasPasswordlessNotificationMessages() => $_has(16);
|
$core.bool hasPasswordlessNotificationMessages() => $_has(14);
|
||||||
@$pb.TagNumber(17)
|
@$pb.TagNumber(17)
|
||||||
void clearPasswordlessNotificationMessages() => $_clearField(17);
|
void clearPasswordlessNotificationMessages() => $_clearField(17);
|
||||||
@$pb.TagNumber(17)
|
@$pb.TagNumber(17)
|
||||||
Response_PasswordlessNotificationMessages
|
Response_PasswordlessNotificationMessages
|
||||||
ensurePasswordlessNotificationMessages() => $_ensure(16);
|
ensurePasswordlessNotificationMessages() => $_ensure(14);
|
||||||
|
|
||||||
@$pb.TagNumber(18)
|
@$pb.TagNumber(18)
|
||||||
Response_MemoriesUploadUrls get memoriesUploadUrls => $_getN(17);
|
Response_MemoriesUploadUrls get memoriesUploadUrls => $_getN(15);
|
||||||
@$pb.TagNumber(18)
|
@$pb.TagNumber(18)
|
||||||
set memoriesUploadUrls(Response_MemoriesUploadUrls value) =>
|
set memoriesUploadUrls(Response_MemoriesUploadUrls value) =>
|
||||||
$_setField(18, value);
|
$_setField(18, value);
|
||||||
@$pb.TagNumber(18)
|
@$pb.TagNumber(18)
|
||||||
$core.bool hasMemoriesUploadUrls() => $_has(17);
|
$core.bool hasMemoriesUploadUrls() => $_has(15);
|
||||||
@$pb.TagNumber(18)
|
@$pb.TagNumber(18)
|
||||||
void clearMemoriesUploadUrls() => $_clearField(18);
|
void clearMemoriesUploadUrls() => $_clearField(18);
|
||||||
@$pb.TagNumber(18)
|
@$pb.TagNumber(18)
|
||||||
Response_MemoriesUploadUrls ensureMemoriesUploadUrls() => $_ensure(17);
|
Response_MemoriesUploadUrls ensureMemoriesUploadUrls() => $_ensure(15);
|
||||||
|
|
||||||
@$pb.TagNumber(19)
|
@$pb.TagNumber(19)
|
||||||
Response_MemoriesList get memoriesList => $_getN(18);
|
Response_MemoriesList get memoriesList => $_getN(16);
|
||||||
@$pb.TagNumber(19)
|
@$pb.TagNumber(19)
|
||||||
set memoriesList(Response_MemoriesList value) => $_setField(19, value);
|
set memoriesList(Response_MemoriesList value) => $_setField(19, value);
|
||||||
@$pb.TagNumber(19)
|
@$pb.TagNumber(19)
|
||||||
$core.bool hasMemoriesList() => $_has(18);
|
$core.bool hasMemoriesList() => $_has(16);
|
||||||
@$pb.TagNumber(19)
|
@$pb.TagNumber(19)
|
||||||
void clearMemoriesList() => $_clearField(19);
|
void clearMemoriesList() => $_clearField(19);
|
||||||
@$pb.TagNumber(19)
|
@$pb.TagNumber(19)
|
||||||
Response_MemoriesList ensureMemoriesList() => $_ensure(18);
|
Response_MemoriesList ensureMemoriesList() => $_ensure(16);
|
||||||
|
|
||||||
@$pb.TagNumber(20)
|
@$pb.TagNumber(20)
|
||||||
Response_MemoriesUrl get memoriesUrl => $_getN(19);
|
Response_MemoriesUrl get memoriesUrl => $_getN(17);
|
||||||
@$pb.TagNumber(20)
|
@$pb.TagNumber(20)
|
||||||
set memoriesUrl(Response_MemoriesUrl value) => $_setField(20, value);
|
set memoriesUrl(Response_MemoriesUrl value) => $_setField(20, value);
|
||||||
@$pb.TagNumber(20)
|
@$pb.TagNumber(20)
|
||||||
$core.bool hasMemoriesUrl() => $_has(19);
|
$core.bool hasMemoriesUrl() => $_has(17);
|
||||||
@$pb.TagNumber(20)
|
@$pb.TagNumber(20)
|
||||||
void clearMemoriesUrl() => $_clearField(20);
|
void clearMemoriesUrl() => $_clearField(20);
|
||||||
@$pb.TagNumber(20)
|
@$pb.TagNumber(20)
|
||||||
Response_MemoriesUrl ensureMemoriesUrl() => $_ensure(19);
|
Response_MemoriesUrl ensureMemoriesUrl() => $_ensure(17);
|
||||||
|
|
||||||
@$pb.TagNumber(21)
|
@$pb.TagNumber(21)
|
||||||
Response_MemoriesUsage get memoriesUsage => $_getN(20);
|
Response_MemoriesUsage get memoriesUsage => $_getN(18);
|
||||||
@$pb.TagNumber(21)
|
@$pb.TagNumber(21)
|
||||||
set memoriesUsage(Response_MemoriesUsage value) => $_setField(21, value);
|
set memoriesUsage(Response_MemoriesUsage value) => $_setField(21, value);
|
||||||
@$pb.TagNumber(21)
|
@$pb.TagNumber(21)
|
||||||
$core.bool hasMemoriesUsage() => $_has(20);
|
$core.bool hasMemoriesUsage() => $_has(18);
|
||||||
@$pb.TagNumber(21)
|
@$pb.TagNumber(21)
|
||||||
void clearMemoriesUsage() => $_clearField(21);
|
void clearMemoriesUsage() => $_clearField(21);
|
||||||
@$pb.TagNumber(21)
|
@$pb.TagNumber(21)
|
||||||
Response_MemoriesUsage ensureMemoriesUsage() => $_ensure(20);
|
Response_MemoriesUsage ensureMemoriesUsage() => $_ensure(18);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Response_Response { ok, error, notSet }
|
enum Response_Response { ok, error, notSet }
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,14 @@ const V0$json = {
|
||||||
'9': 0,
|
'9': 0,
|
||||||
'10': 'RequestNewPreKeys'
|
'10': 'RequestNewPreKeys'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'1': 'RequestNewPqcPreKeys',
|
||||||
|
'3': 8,
|
||||||
|
'4': 1,
|
||||||
|
'5': 8,
|
||||||
|
'9': 0,
|
||||||
|
'10': 'RequestNewPqcPreKeys'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'1': 'error',
|
'1': 'error',
|
||||||
'3': 6,
|
'3': 6,
|
||||||
|
|
@ -100,8 +108,9 @@ final $typed_data.Uint8List v0Descriptor = $convert.base64Decode(
|
||||||
'llbnQuUmVzcG9uc2VIAFIIcmVzcG9uc2USPgoKbmV3TWVzc2FnZRgDIAEoCzIcLnNlcnZlcl90'
|
'llbnQuUmVzcG9uc2VIAFIIcmVzcG9uc2USPgoKbmV3TWVzc2FnZRgDIAEoCzIcLnNlcnZlcl90'
|
||||||
'b19jbGllbnQuTmV3TWVzc2FnZUgAUgpuZXdNZXNzYWdlEkEKC25ld01lc3NhZ2VzGAcgASgLMh'
|
'b19jbGllbnQuTmV3TWVzc2FnZUgAUgpuZXdNZXNzYWdlEkEKC25ld01lc3NhZ2VzGAcgASgLMh'
|
||||||
'0uc2VydmVyX3RvX2NsaWVudC5OZXdNZXNzYWdlc0gAUgtuZXdNZXNzYWdlcxIuChFSZXF1ZXN0'
|
'0uc2VydmVyX3RvX2NsaWVudC5OZXdNZXNzYWdlc0gAUgtuZXdNZXNzYWdlcxIuChFSZXF1ZXN0'
|
||||||
'TmV3UHJlS2V5cxgEIAEoCEgAUhFSZXF1ZXN0TmV3UHJlS2V5cxIoCgVlcnJvchgGIAEoDjIQLm'
|
'TmV3UHJlS2V5cxgEIAEoCEgAUhFSZXF1ZXN0TmV3UHJlS2V5cxI0ChRSZXF1ZXN0TmV3UHFjUH'
|
||||||
'Vycm9yLkVycm9yQ29kZUgAUgVlcnJvckIGCgRLaW5k');
|
'JlS2V5cxgIIAEoCEgAUhRSZXF1ZXN0TmV3UHFjUHJlS2V5cxIoCgVlcnJvchgGIAEoDjIQLmVy'
|
||||||
|
'cm9yLkVycm9yQ29kZUgAUgVlcnJvckIGCgRLaW5k');
|
||||||
|
|
||||||
@$core.Deprecated('Use newMessageDescriptor instead')
|
@$core.Deprecated('Use newMessageDescriptor instead')
|
||||||
const NewMessage$json = {
|
const NewMessage$json = {
|
||||||
|
|
@ -167,11 +176,12 @@ const Response$json = {
|
||||||
Response_AddAccountsInvite$json,
|
Response_AddAccountsInvite$json,
|
||||||
Response_AddAccountsInvites$json,
|
Response_AddAccountsInvites$json,
|
||||||
Response_AdditionalAccount$json,
|
Response_AdditionalAccount$json,
|
||||||
Response_Deprecated$json,
|
|
||||||
Response_Transaction$json,
|
Response_Transaction$json,
|
||||||
Response_PlanBallance$json,
|
Response_PlanBallance$json,
|
||||||
Response_PreKey$json,
|
Response_PreKey$json,
|
||||||
Response_SignedPreKey$json,
|
Response_SignedPreKey$json,
|
||||||
|
Response_PqcPreKey$json,
|
||||||
|
Response_PqcBundle$json,
|
||||||
Response_UserData$json,
|
Response_UserData$json,
|
||||||
Response_UploadToken$json,
|
Response_UploadToken$json,
|
||||||
Response_DownloadTokens$json,
|
Response_DownloadTokens$json,
|
||||||
|
|
@ -306,11 +316,6 @@ const Response_AdditionalAccount$json = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
@$core.Deprecated('Use responseDescriptor instead')
|
|
||||||
const Response_Deprecated$json = {
|
|
||||||
'1': 'Deprecated',
|
|
||||||
};
|
|
||||||
|
|
||||||
@$core.Deprecated('Use responseDescriptor instead')
|
@$core.Deprecated('Use responseDescriptor instead')
|
||||||
const Response_Transaction$json = {
|
const Response_Transaction$json = {
|
||||||
'1': 'Transaction',
|
'1': 'Transaction',
|
||||||
|
|
@ -420,6 +425,86 @@ const Response_SignedPreKey$json = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use responseDescriptor instead')
|
||||||
|
const Response_PqcPreKey$json = {
|
||||||
|
'1': 'PqcPreKey',
|
||||||
|
'2': [
|
||||||
|
{'1': 'ecc_pre_key_id', '3': 1, '4': 1, '5': 3, '10': 'eccPreKeyId'},
|
||||||
|
{'1': 'ecc_pre_key', '3': 2, '4': 1, '5': 12, '10': 'eccPreKey'},
|
||||||
|
{'1': 'kyber_pre_key_id', '3': 3, '4': 1, '5': 3, '10': 'kyberPreKeyId'},
|
||||||
|
{'1': 'kyber_pre_key', '3': 4, '4': 1, '5': 12, '10': 'kyberPreKey'},
|
||||||
|
{
|
||||||
|
'1': 'kyber_pre_key_signature',
|
||||||
|
'3': 5,
|
||||||
|
'4': 1,
|
||||||
|
'5': 12,
|
||||||
|
'10': 'kyberPreKeySignature'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use responseDescriptor instead')
|
||||||
|
const Response_PqcBundle$json = {
|
||||||
|
'1': 'PqcBundle',
|
||||||
|
'2': [
|
||||||
|
{
|
||||||
|
'1': 'ecc_signed_prekey_id',
|
||||||
|
'3': 1,
|
||||||
|
'4': 1,
|
||||||
|
'5': 3,
|
||||||
|
'10': 'eccSignedPrekeyId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'1': 'ecc_signed_prekey',
|
||||||
|
'3': 2,
|
||||||
|
'4': 1,
|
||||||
|
'5': 12,
|
||||||
|
'10': 'eccSignedPrekey'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'1': 'ecc_signed_prekey_signature',
|
||||||
|
'3': 3,
|
||||||
|
'4': 1,
|
||||||
|
'5': 12,
|
||||||
|
'10': 'eccSignedPrekeySignature'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'1': 'kyber_signed_prekey_id',
|
||||||
|
'3': 4,
|
||||||
|
'4': 1,
|
||||||
|
'5': 3,
|
||||||
|
'10': 'kyberSignedPrekeyId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'1': 'kyber_signed_prekey',
|
||||||
|
'3': 5,
|
||||||
|
'4': 1,
|
||||||
|
'5': 12,
|
||||||
|
'10': 'kyberSignedPrekey'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'1': 'kyber_signed_prekey_signature',
|
||||||
|
'3': 6,
|
||||||
|
'4': 1,
|
||||||
|
'5': 12,
|
||||||
|
'10': 'kyberSignedPrekeySignature'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'1': 'prekey',
|
||||||
|
'3': 7,
|
||||||
|
'4': 1,
|
||||||
|
'5': 11,
|
||||||
|
'6': '.server_to_client.Response.PqcPreKey',
|
||||||
|
'9': 0,
|
||||||
|
'10': 'prekey',
|
||||||
|
'17': true
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'8': [
|
||||||
|
{'1': '_prekey'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
@$core.Deprecated('Use responseDescriptor instead')
|
@$core.Deprecated('Use responseDescriptor instead')
|
||||||
const Response_UserData$json = {
|
const Response_UserData$json = {
|
||||||
'1': 'UserData',
|
'1': 'UserData',
|
||||||
|
|
@ -487,6 +572,16 @@ const Response_UserData$json = {
|
||||||
'10': 'registrationId',
|
'10': 'registrationId',
|
||||||
'17': true
|
'17': true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'1': 'pqc_bundle',
|
||||||
|
'3': 9,
|
||||||
|
'4': 1,
|
||||||
|
'5': 11,
|
||||||
|
'6': '.server_to_client.Response.PqcBundle',
|
||||||
|
'9': 6,
|
||||||
|
'10': 'pqcBundle',
|
||||||
|
'17': true
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'8': [
|
'8': [
|
||||||
{'1': '_username'},
|
{'1': '_username'},
|
||||||
|
|
@ -495,6 +590,7 @@ const Response_UserData$json = {
|
||||||
{'1': '_signed_prekey_signature'},
|
{'1': '_signed_prekey_signature'},
|
||||||
{'1': '_signed_prekey_id'},
|
{'1': '_signed_prekey_id'},
|
||||||
{'1': '_registration_id'},
|
{'1': '_registration_id'},
|
||||||
|
{'1': '_pqc_bundle'},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -687,15 +783,6 @@ const Response_Ok$json = {
|
||||||
'10': 'userdata'
|
'10': 'userdata'
|
||||||
},
|
},
|
||||||
{'1': 'authtoken', '3': 6, '4': 1, '5': 12, '9': 0, '10': 'authtoken'},
|
{'1': 'authtoken', '3': 6, '4': 1, '5': 12, '9': 0, '10': 'authtoken'},
|
||||||
{
|
|
||||||
'1': 'deprecated_7',
|
|
||||||
'3': 7,
|
|
||||||
'4': 1,
|
|
||||||
'5': 11,
|
|
||||||
'6': '.server_to_client.Response.Deprecated',
|
|
||||||
'9': 0,
|
|
||||||
'10': 'deprecated7'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'1': 'authenticated',
|
'1': 'authenticated',
|
||||||
'3': 8,
|
'3': 8,
|
||||||
|
|
@ -723,15 +810,6 @@ const Response_Ok$json = {
|
||||||
'9': 0,
|
'9': 0,
|
||||||
'10': 'planballance'
|
'10': 'planballance'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
'1': 'deprecated_11',
|
|
||||||
'3': 11,
|
|
||||||
'4': 1,
|
|
||||||
'5': 11,
|
|
||||||
'6': '.server_to_client.Response.Deprecated',
|
|
||||||
'9': 0,
|
|
||||||
'10': 'deprecated11'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'1': 'addaccountsinvites',
|
'1': 'addaccountsinvites',
|
||||||
'3': 12,
|
'3': 12,
|
||||||
|
|
@ -825,6 +903,10 @@ const Response_Ok$json = {
|
||||||
'8': [
|
'8': [
|
||||||
{'1': 'Ok'},
|
{'1': 'Ok'},
|
||||||
],
|
],
|
||||||
|
'9': [
|
||||||
|
{'1': 7, '2': 8},
|
||||||
|
{'1': 11, '2': 12},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Descriptor for `Response`. Decode as a `google.protobuf.DescriptorProto`.
|
/// Descriptor for `Response`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
|
|
@ -846,79 +928,89 @@ final $typed_data.Uint8List responseDescriptor = $convert.base64Decode(
|
||||||
'cGxhbklkEh8KC2ludml0ZV9jb2RlGAIgASgJUgppbnZpdGVDb2RlGlwKEkFkZEFjY291bnRzSW'
|
'cGxhbklkEh8KC2ludml0ZV9jb2RlGAIgASgJUgppbnZpdGVDb2RlGlwKEkFkZEFjY291bnRzSW'
|
||||||
'52aXRlcxJGCgdpbnZpdGVzGAEgAygLMiwuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5BZGRB'
|
'52aXRlcxJGCgdpbnZpdGVzGAEgAygLMiwuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5BZGRB'
|
||||||
'Y2NvdW50c0ludml0ZVIHaW52aXRlcxpFChFBZGRpdGlvbmFsQWNjb3VudBIXCgd1c2VyX2lkGA'
|
'Y2NvdW50c0ludml0ZVIHaW52aXRlcxpFChFBZGRpdGlvbmFsQWNjb3VudBIXCgd1c2VyX2lkGA'
|
||||||
'EgASgDUgZ1c2VySWQSFwoHcGxhbl9pZBgDIAEoCVIGcGxhbklkGgwKCkRlcHJlY2F0ZWQaDQoL'
|
'EgASgDUgZ1c2VySWQSFwoHcGxhbl9pZBgDIAEoCVIGcGxhbklkGg0KC1RyYW5zYWN0aW9uGpcF'
|
||||||
'VHJhbnNhY3Rpb24alwUKDFBsYW5CYWxsYW5jZRJACh11c2VkX2RhaWx5X21lZGlhX3VwbG9hZF'
|
'CgxQbGFuQmFsbGFuY2USQAoddXNlZF9kYWlseV9tZWRpYV91cGxvYWRfbGltaXQYASABKANSGX'
|
||||||
'9saW1pdBgBIAEoA1IZdXNlZERhaWx5TWVkaWFVcGxvYWRMaW1pdBI+Chx1c2VkX3VwbG9hZF9t'
|
'VzZWREYWlseU1lZGlhVXBsb2FkTGltaXQSPgocdXNlZF91cGxvYWRfbWVkaWFfc2l6ZV9saW1p'
|
||||||
'ZWRpYV9zaXplX2xpbWl0GAIgASgDUhh1c2VkVXBsb2FkTWVkaWFTaXplTGltaXQSMwoTcGF5bW'
|
'dBgCIAEoA1IYdXNlZFVwbG9hZE1lZGlhU2l6ZUxpbWl0EjMKE3BheW1lbnRfcGVyaW9kX2RheX'
|
||||||
'VudF9wZXJpb2RfZGF5cxgDIAEoA0gAUhFwYXltZW50UGVyaW9kRGF5c4gBARJLCiBsYXN0X3Bh'
|
'MYAyABKANIAFIRcGF5bWVudFBlcmlvZERheXOIAQESSwogbGFzdF9wYXltZW50X2RvbmVfdW5p'
|
||||||
'eW1lbnRfZG9uZV91bml4X3RpbWVzdGFtcBgEIAEoA0gBUhxsYXN0UGF5bWVudERvbmVVbml4VG'
|
'eF90aW1lc3RhbXAYBCABKANIAVIcbGFzdFBheW1lbnREb25lVW5peFRpbWVzdGFtcIgBARJKCg'
|
||||||
'ltZXN0YW1wiAEBEkoKDHRyYW5zYWN0aW9ucxgFIAMoCzImLnNlcnZlcl90b19jbGllbnQuUmVz'
|
'x0cmFuc2FjdGlvbnMYBSADKAsyJi5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlRyYW5zYWN0'
|
||||||
'cG9uc2UuVHJhbnNhY3Rpb25SDHRyYW5zYWN0aW9ucxJdChNhZGRpdGlvbmFsX2FjY291bnRzGA'
|
'aW9uUgx0cmFuc2FjdGlvbnMSXQoTYWRkaXRpb25hbF9hY2NvdW50cxgGIAMoCzIsLnNlcnZlcl'
|
||||||
'YgAygLMiwuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5BZGRpdGlvbmFsQWNjb3VudFISYWRk'
|
'90b19jbGllbnQuUmVzcG9uc2UuQWRkaXRpb25hbEFjY291bnRSEmFkZGl0aW9uYWxBY2NvdW50'
|
||||||
'aXRpb25hbEFjY291bnRzEiYKDGF1dG9fcmVuZXdhbBgHIAEoCEgCUgthdXRvUmVuZXdhbIgBAR'
|
'cxImCgxhdXRvX3JlbmV3YWwYByABKAhIAlILYXV0b1JlbmV3YWyIAQESQgobYWRkaXRpb25hbF'
|
||||||
'JCChthZGRpdGlvbmFsX2FjY291bnRfb3duZXJfaWQYCCABKANIA1IYYWRkaXRpb25hbEFjY291'
|
'9hY2NvdW50X293bmVyX2lkGAggASgDSANSGGFkZGl0aW9uYWxBY2NvdW50T3duZXJJZIgBAUIW'
|
||||||
'bnRPd25lcklkiAEBQhYKFF9wYXltZW50X3BlcmlvZF9kYXlzQiMKIV9sYXN0X3BheW1lbnRfZG'
|
'ChRfcGF5bWVudF9wZXJpb2RfZGF5c0IjCiFfbGFzdF9wYXltZW50X2RvbmVfdW5peF90aW1lc3'
|
||||||
'9uZV91bml4X3RpbWVzdGFtcEIPCg1fYXV0b19yZW5ld2FsQh4KHF9hZGRpdGlvbmFsX2FjY291'
|
'RhbXBCDwoNX2F1dG9fcmVuZXdhbEIeChxfYWRkaXRpb25hbF9hY2NvdW50X293bmVyX2lkGjAK'
|
||||||
'bnRfb3duZXJfaWQaMAoGUHJlS2V5Eg4KAmlkGAEgASgDUgJpZBIWCgZwcmVrZXkYAiABKAxSBn'
|
'BlByZUtleRIOCgJpZBgBIAEoA1ICaWQSFgoGcHJla2V5GAIgASgMUgZwcmVrZXkalQEKDFNpZ2'
|
||||||
'ByZWtleRqVAQoMU2lnbmVkUHJlS2V5EigKEHNpZ25lZF9wcmVrZXlfaWQYASABKANSDnNpZ25l'
|
'5lZFByZUtleRIoChBzaWduZWRfcHJla2V5X2lkGAEgASgDUg5zaWduZWRQcmVrZXlJZBIjCg1z'
|
||||||
'ZFByZWtleUlkEiMKDXNpZ25lZF9wcmVrZXkYAiABKAxSDHNpZ25lZFByZWtleRI2ChdzaWduZW'
|
'aWduZWRfcHJla2V5GAIgASgMUgxzaWduZWRQcmVrZXkSNgoXc2lnbmVkX3ByZWtleV9zaWduYX'
|
||||||
'RfcHJla2V5X3NpZ25hdHVyZRgDIAEoDFIVc2lnbmVkUHJla2V5U2lnbmF0dXJlGvYDCghVc2Vy'
|
'R1cmUYAyABKAxSFXNpZ25lZFByZWtleVNpZ25hdHVyZRrUAQoJUHFjUHJlS2V5EiMKDmVjY19w'
|
||||||
'RGF0YRIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQSOwoHcHJla2V5cxgCIAMoCzIhLnNlcnZlcl'
|
'cmVfa2V5X2lkGAEgASgDUgtlY2NQcmVLZXlJZBIeCgtlY2NfcHJlX2tleRgCIAEoDFIJZWNjUH'
|
||||||
'90b19jbGllbnQuUmVzcG9uc2UuUHJlS2V5UgdwcmVrZXlzEh8KCHVzZXJuYW1lGAcgASgMSABS'
|
'JlS2V5EicKEGt5YmVyX3ByZV9rZXlfaWQYAyABKANSDWt5YmVyUHJlS2V5SWQSIgoNa3liZXJf'
|
||||||
'CHVzZXJuYW1liAEBEjMKE3B1YmxpY19pZGVudGl0eV9rZXkYAyABKAxIAVIRcHVibGljSWRlbn'
|
'cHJlX2tleRgEIAEoDFILa3liZXJQcmVLZXkSNQoXa3liZXJfcHJlX2tleV9zaWduYXR1cmUYBS'
|
||||||
'RpdHlLZXmIAQESKAoNc2lnbmVkX3ByZWtleRgEIAEoDEgCUgxzaWduZWRQcmVrZXmIAQESOwoX'
|
'ABKAxSFGt5YmVyUHJlS2V5U2lnbmF0dXJlGp0DCglQcWNCdW5kbGUSLwoUZWNjX3NpZ25lZF9w'
|
||||||
'c2lnbmVkX3ByZWtleV9zaWduYXR1cmUYBSABKAxIA1IVc2lnbmVkUHJla2V5U2lnbmF0dXJliA'
|
'cmVrZXlfaWQYASABKANSEWVjY1NpZ25lZFByZWtleUlkEioKEWVjY19zaWduZWRfcHJla2V5GA'
|
||||||
'EBEi0KEHNpZ25lZF9wcmVrZXlfaWQYBiABKANIBFIOc2lnbmVkUHJla2V5SWSIAQESLAoPcmVn'
|
'IgASgMUg9lY2NTaWduZWRQcmVrZXkSPQobZWNjX3NpZ25lZF9wcmVrZXlfc2lnbmF0dXJlGAMg'
|
||||||
'aXN0cmF0aW9uX2lkGAggASgDSAVSDnJlZ2lzdHJhdGlvbklkiAEBQgsKCV91c2VybmFtZUIWCh'
|
'ASgMUhhlY2NTaWduZWRQcmVrZXlTaWduYXR1cmUSMwoWa3liZXJfc2lnbmVkX3ByZWtleV9pZB'
|
||||||
'RfcHVibGljX2lkZW50aXR5X2tleUIQCg5fc2lnbmVkX3ByZWtleUIaChhfc2lnbmVkX3ByZWtl'
|
'gEIAEoA1ITa3liZXJTaWduZWRQcmVrZXlJZBIuChNreWJlcl9zaWduZWRfcHJla2V5GAUgASgM'
|
||||||
'eV9zaWduYXR1cmVCEwoRX3NpZ25lZF9wcmVrZXlfaWRCEgoQX3JlZ2lzdHJhdGlvbl9pZBpZCg'
|
'UhFreWJlclNpZ25lZFByZWtleRJBCh1reWJlcl9zaWduZWRfcHJla2V5X3NpZ25hdHVyZRgGIA'
|
||||||
'tVcGxvYWRUb2tlbhIhCgx1cGxvYWRfdG9rZW4YASABKAxSC3VwbG9hZFRva2VuEicKD2Rvd25s'
|
'EoDFIaa3liZXJTaWduZWRQcmVrZXlTaWduYXR1cmUSQQoGcHJla2V5GAcgASgLMiQuc2VydmVy'
|
||||||
'b2FkX3Rva2VucxgCIAMoDFIOZG93bmxvYWRUb2tlbnMaOQoORG93bmxvYWRUb2tlbnMSJwoPZG'
|
'X3RvX2NsaWVudC5SZXNwb25zZS5QcWNQcmVLZXlIAFIGcHJla2V5iAEBQgkKB19wcmVrZXkazw'
|
||||||
'93bmxvYWRfdG9rZW5zGAEgAygMUg5kb3dubG9hZFRva2VucxpFCgtQcm9vZk9mV29yaxIWCgZw'
|
'QKCFVzZXJEYXRhEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBI7CgdwcmVrZXlzGAIgAygLMiEu'
|
||||||
'cmVmaXgYASABKAlSBnByZWZpeBIeCgpkaWZmaWN1bHR5GAIgASgDUgpkaWZmaWN1bHR5Gl4KH1'
|
'c2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5QcmVLZXlSB3ByZWtleXMSHwoIdXNlcm5hbWUYBy'
|
||||||
'Bhc3N3b3JkbGVzc05vdGlmaWNhdGlvbk1lc3NhZ2USDgoCaWQYASABKANSAmlkEisKEWVuY3J5'
|
'ABKAxIAFIIdXNlcm5hbWWIAQESMwoTcHVibGljX2lkZW50aXR5X2tleRgDIAEoDEgBUhFwdWJs'
|
||||||
'cHRlZF9tZXNzYWdlGAIgASgMUhBlbmNyeXB0ZWRNZXNzYWdlGnoKIFBhc3N3b3JkbGVzc05vdG'
|
'aWNJZGVudGl0eUtleYgBARIoCg1zaWduZWRfcHJla2V5GAQgASgMSAJSDHNpZ25lZFByZWtleY'
|
||||||
'lmaWNhdGlvbk1lc3NhZ2VzElYKCG1lc3NhZ2VzGAEgAygLMjouc2VydmVyX3RvX2NsaWVudC5S'
|
'gBARI7ChdzaWduZWRfcHJla2V5X3NpZ25hdHVyZRgFIAEoDEgDUhVzaWduZWRQcmVrZXlTaWdu'
|
||||||
'ZXNwb25zZS5QYXNzd29yZGxlc3NOb3RpZmljYXRpb25NZXNzYWdlUghtZXNzYWdlcxqqAQoNUH'
|
'YXR1cmWIAQESLQoQc2lnbmVkX3ByZWtleV9pZBgGIAEoA0gEUg5zaWduZWRQcmVrZXlJZIgBAR'
|
||||||
'Jlc2lnbmVkUG9zdBIQCgN1cmwYASABKAlSA3VybBJMCgZmaWVsZHMYAiADKAsyNC5zZXJ2ZXJf'
|
'IsCg9yZWdpc3RyYXRpb25faWQYCCABKANIBVIOcmVnaXN0cmF0aW9uSWSIAQESSAoKcHFjX2J1'
|
||||||
'dG9fY2xpZW50LlJlc3BvbnNlLlByZXNpZ25lZFBvc3QuRmllbGRzRW50cnlSBmZpZWxkcxo5Cg'
|
'bmRsZRgJIAEoCzIkLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuUHFjQnVuZGxlSAZSCXBxY0'
|
||||||
'tGaWVsZHNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWU6AjgB'
|
'J1bmRsZYgBAUILCglfdXNlcm5hbWVCFgoUX3B1YmxpY19pZGVudGl0eV9rZXlCEAoOX3NpZ25l'
|
||||||
'Gs8BChJNZW1vcmllc1VwbG9hZFVybHMSGQoIbWVkaWFfaWQYASABKAlSB21lZGlhSWQSUwoQdG'
|
'ZF9wcmVrZXlCGgoYX3NpZ25lZF9wcmVrZXlfc2lnbmF0dXJlQhMKEV9zaWduZWRfcHJla2V5X2'
|
||||||
'h1bWJuYWlsX3VwbG9hZBgCIAEoCzIoLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuUHJlc2ln'
|
'lkQhIKEF9yZWdpc3RyYXRpb25faWRCDQoLX3BxY19idW5kbGUaWQoLVXBsb2FkVG9rZW4SIQoM'
|
||||||
'bmVkUG9zdFIPdGh1bWJuYWlsVXBsb2FkEkkKC2Z1bGxfdXBsb2FkGAMgASgLMiguc2VydmVyX3'
|
'dXBsb2FkX3Rva2VuGAEgASgMUgt1cGxvYWRUb2tlbhInCg9kb3dubG9hZF90b2tlbnMYAiADKA'
|
||||||
'RvX2NsaWVudC5SZXNwb25zZS5QcmVzaWduZWRQb3N0UgpmdWxsVXBsb2FkGoEBCglNZWRpYUl0'
|
'xSDmRvd25sb2FkVG9rZW5zGjkKDkRvd25sb2FkVG9rZW5zEicKD2Rvd25sb2FkX3Rva2VucxgB'
|
||||||
'ZW0SGQoIbWVkaWFfaWQYASABKAlSB21lZGlhSWQSIwoNb3JpZ2luYWxfZGF0ZRgCIAEoA1IMb3'
|
'IAMoDFIOZG93bmxvYWRUb2tlbnMaRQoLUHJvb2ZPZldvcmsSFgoGcHJlZml4GAEgASgJUgZwcm'
|
||||||
'JpZ2luYWxEYXRlEjQKFnRodW1ibmFpbF9kb3dubG9hZF91cmwYAyABKAlSFHRodW1ibmFpbERv'
|
'VmaXgSHgoKZGlmZmljdWx0eRgCIAEoA1IKZGlmZmljdWx0eRpeCh9QYXNzd29yZGxlc3NOb3Rp'
|
||||||
'd25sb2FkVXJsGkoKDE1lbW9yaWVzTGlzdBI6CgVpdGVtcxgBIAMoCzIkLnNlcnZlcl90b19jbG'
|
'ZmljYXRpb25NZXNzYWdlEg4KAmlkGAEgASgDUgJpZBIrChFlbmNyeXB0ZWRfbWVzc2FnZRgCIA'
|
||||||
'llbnQuUmVzcG9uc2UuTWVkaWFJdGVtUgVpdGVtcxo5CgtNZW1vcmllc1VybBIqChFmdWxsX2Rv'
|
'EoDFIQZW5jcnlwdGVkTWVzc2FnZRp6CiBQYXNzd29yZGxlc3NOb3RpZmljYXRpb25NZXNzYWdl'
|
||||||
'd25sb2FkX3VybBgBIAEoCVIPZnVsbERvd25sb2FkVXJsGmcKDU1lbW9yaWVzVXNhZ2USGwoJbW'
|
'cxJWCghtZXNzYWdlcxgBIAMoCzI6LnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuUGFzc3dvcm'
|
||||||
'F4X2J5dGVzGAEgASgDUghtYXhCeXRlcxIjCg1jdXJyZW50X2J5dGVzGAIgASgDUgxjdXJyZW50'
|
'RsZXNzTm90aWZpY2F0aW9uTWVzc2FnZVIIbWVzc2FnZXMaqgEKDVByZXNpZ25lZFBvc3QSEAoD'
|
||||||
'Qnl0ZXMSFAoFY291bnQYAyABKANSBWNvdW50GoMMCgJPaxIUCgROb25lGAEgASgISABSBE5vbm'
|
'dXJsGAEgASgJUgN1cmwSTAoGZmllbGRzGAIgAygLMjQuc2VydmVyX3RvX2NsaWVudC5SZXNwb2'
|
||||||
'USGAoGdXNlcmlkGAIgASgDSABSBnVzZXJpZBImCg1hdXRoY2hhbGxlbmdlGAMgASgMSABSDWF1'
|
'5zZS5QcmVzaWduZWRQb3N0LkZpZWxkc0VudHJ5UgZmaWVsZHMaOQoLRmllbGRzRW50cnkSEAoD'
|
||||||
'dGhjaGFsbGVuZ2USSgoLdXBsb2FkdG9rZW4YBCABKAsyJi5zZXJ2ZXJfdG9fY2xpZW50LlJlc3'
|
'a2V5GAEgASgJUgNrZXkSFAoFdmFsdWUYAiABKAlSBXZhbHVlOgI4ARrPAQoSTWVtb3JpZXNVcG'
|
||||||
'BvbnNlLlVwbG9hZFRva2VuSABSC3VwbG9hZHRva2VuEkEKCHVzZXJkYXRhGAUgASgLMiMuc2Vy'
|
'xvYWRVcmxzEhkKCG1lZGlhX2lkGAEgASgJUgdtZWRpYUlkElMKEHRodW1ibmFpbF91cGxvYWQY'
|
||||||
'dmVyX3RvX2NsaWVudC5SZXNwb25zZS5Vc2VyRGF0YUgAUgh1c2VyZGF0YRIeCglhdXRodG9rZW'
|
'AiABKAsyKC5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlByZXNpZ25lZFBvc3RSD3RodW1ibm'
|
||||||
'4YBiABKAxIAFIJYXV0aHRva2VuEkoKDGRlcHJlY2F0ZWRfNxgHIAEoCzIlLnNlcnZlcl90b19j'
|
'FpbFVwbG9hZBJJCgtmdWxsX3VwbG9hZBgDIAEoCzIoLnNlcnZlcl90b19jbGllbnQuUmVzcG9u'
|
||||||
'bGllbnQuUmVzcG9uc2UuRGVwcmVjYXRlZEgAUgtkZXByZWNhdGVkNxJQCg1hdXRoZW50aWNhdG'
|
'c2UuUHJlc2lnbmVkUG9zdFIKZnVsbFVwbG9hZBqBAQoJTWVkaWFJdGVtEhkKCG1lZGlhX2lkGA'
|
||||||
'VkGAggASgLMiguc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5BdXRoZW50aWNhdGVkSABSDWF1'
|
'EgASgJUgdtZWRpYUlkEiMKDW9yaWdpbmFsX2RhdGUYAiABKANSDG9yaWdpbmFsRGF0ZRI0ChZ0'
|
||||||
'dGhlbnRpY2F0ZWQSOAoFcGxhbnMYCSABKAsyIC5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLl'
|
'aHVtYm5haWxfZG93bmxvYWRfdXJsGAMgASgJUhR0aHVtYm5haWxEb3dubG9hZFVybBpKCgxNZW'
|
||||||
'BsYW5zSABSBXBsYW5zEk0KDHBsYW5iYWxsYW5jZRgKIAEoCzInLnNlcnZlcl90b19jbGllbnQu'
|
'1vcmllc0xpc3QSOgoFaXRlbXMYASADKAsyJC5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLk1l'
|
||||||
'UmVzcG9uc2UuUGxhbkJhbGxhbmNlSABSDHBsYW5iYWxsYW5jZRJMCg1kZXByZWNhdGVkXzExGA'
|
'ZGlhSXRlbVIFaXRlbXMaOQoLTWVtb3JpZXNVcmwSKgoRZnVsbF9kb3dubG9hZF91cmwYASABKA'
|
||||||
'sgASgLMiUuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5EZXByZWNhdGVkSABSDGRlcHJlY2F0'
|
'lSD2Z1bGxEb3dubG9hZFVybBpnCg1NZW1vcmllc1VzYWdlEhsKCW1heF9ieXRlcxgBIAEoA1II'
|
||||||
'ZWQxMRJfChJhZGRhY2NvdW50c2ludml0ZXMYDCABKAsyLS5zZXJ2ZXJfdG9fY2xpZW50LlJlc3'
|
'bWF4Qnl0ZXMSIwoNY3VycmVudF9ieXRlcxgCIAEoA1IMY3VycmVudEJ5dGVzEhQKBWNvdW50GA'
|
||||||
'BvbnNlLkFkZEFjY291bnRzSW52aXRlc0gAUhJhZGRhY2NvdW50c2ludml0ZXMSUwoOZG93bmxv'
|
'MgASgDUgVjb3VudBr1CgoCT2sSFAoETm9uZRgBIAEoCEgAUgROb25lEhgKBnVzZXJpZBgCIAEo'
|
||||||
'YWR0b2tlbnMYDSABKAsyKS5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLkRvd25sb2FkVG9rZW'
|
'A0gAUgZ1c2VyaWQSJgoNYXV0aGNoYWxsZW5nZRgDIAEoDEgAUg1hdXRoY2hhbGxlbmdlEkoKC3'
|
||||||
'5zSABSDmRvd25sb2FkdG9rZW5zEk0KDHNpZ25lZHByZWtleRgOIAEoCzInLnNlcnZlcl90b19j'
|
'VwbG9hZHRva2VuGAQgASgLMiYuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5VcGxvYWRUb2tl'
|
||||||
'bGllbnQuUmVzcG9uc2UuU2lnbmVkUHJlS2V5SABSDHNpZ25lZHByZWtleRJKCgtwcm9vZk9mV2'
|
'bkgAUgt1cGxvYWR0b2tlbhJBCgh1c2VyZGF0YRgFIAEoCzIjLnNlcnZlcl90b19jbGllbnQuUm'
|
||||||
'9yaxgPIAEoCzImLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuUHJvb2ZPZldvcmtIAFILcHJv'
|
'VzcG9uc2UuVXNlckRhdGFIAFIIdXNlcmRhdGESHgoJYXV0aHRva2VuGAYgASgMSABSCWF1dGh0'
|
||||||
'b2ZPZldvcmsSSQogcGFzc3dvcmRsZXNzX3JlY292ZXJ5X3NlcnZlcl9rZXkYECABKAxIAFIdcG'
|
'b2tlbhJQCg1hdXRoZW50aWNhdGVkGAggASgLMiguc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS'
|
||||||
'Fzc3dvcmRsZXNzUmVjb3ZlcnlTZXJ2ZXJLZXkSiwEKInBhc3N3b3JkbGVzc19ub3RpZmljYXRp'
|
'5BdXRoZW50aWNhdGVkSABSDWF1dGhlbnRpY2F0ZWQSOAoFcGxhbnMYCSABKAsyIC5zZXJ2ZXJf'
|
||||||
'b25fbWVzc2FnZXMYESABKAsyOy5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlBhc3N3b3JkbG'
|
'dG9fY2xpZW50LlJlc3BvbnNlLlBsYW5zSABSBXBsYW5zEk0KDHBsYW5iYWxsYW5jZRgKIAEoCz'
|
||||||
'Vzc05vdGlmaWNhdGlvbk1lc3NhZ2VzSABSIHBhc3N3b3JkbGVzc05vdGlmaWNhdGlvbk1lc3Nh'
|
'InLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuUGxhbkJhbGxhbmNlSABSDHBsYW5iYWxsYW5j'
|
||||||
'Z2VzEmEKFG1lbW9yaWVzX3VwbG9hZF91cmxzGBIgASgLMi0uc2VydmVyX3RvX2NsaWVudC5SZX'
|
'ZRJfChJhZGRhY2NvdW50c2ludml0ZXMYDCABKAsyLS5zZXJ2ZXJfdG9fY2xpZW50LlJlc3Bvbn'
|
||||||
'Nwb25zZS5NZW1vcmllc1VwbG9hZFVybHNIAFISbWVtb3JpZXNVcGxvYWRVcmxzEk4KDW1lbW9y'
|
'NlLkFkZEFjY291bnRzSW52aXRlc0gAUhJhZGRhY2NvdW50c2ludml0ZXMSUwoOZG93bmxvYWR0'
|
||||||
'aWVzX2xpc3QYEyABKAsyJy5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLk1lbW9yaWVzTGlzdE'
|
'b2tlbnMYDSABKAsyKS5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLkRvd25sb2FkVG9rZW5zSA'
|
||||||
'gAUgxtZW1vcmllc0xpc3QSSwoMbWVtb3JpZXNfdXJsGBQgASgLMiYuc2VydmVyX3RvX2NsaWVu'
|
'BSDmRvd25sb2FkdG9rZW5zEk0KDHNpZ25lZHByZWtleRgOIAEoCzInLnNlcnZlcl90b19jbGll'
|
||||||
'dC5SZXNwb25zZS5NZW1vcmllc1VybEgAUgttZW1vcmllc1VybBJRCg5tZW1vcmllc191c2FnZR'
|
'bnQuUmVzcG9uc2UuU2lnbmVkUHJlS2V5SABSDHNpZ25lZHByZWtleRJKCgtwcm9vZk9mV29yax'
|
||||||
'gVIAEoCzIoLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuTWVtb3JpZXNVc2FnZUgAUg1tZW1v'
|
'gPIAEoCzImLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuUHJvb2ZPZldvcmtIAFILcHJvb2ZP'
|
||||||
'cmllc1VzYWdlQgQKAk9rQgoKCFJlc3BvbnNl');
|
'ZldvcmsSSQogcGFzc3dvcmRsZXNzX3JlY292ZXJ5X3NlcnZlcl9rZXkYECABKAxIAFIdcGFzc3'
|
||||||
|
'dvcmRsZXNzUmVjb3ZlcnlTZXJ2ZXJLZXkSiwEKInBhc3N3b3JkbGVzc19ub3RpZmljYXRpb25f'
|
||||||
|
'bWVzc2FnZXMYESABKAsyOy5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlBhc3N3b3JkbGVzc0'
|
||||||
|
'5vdGlmaWNhdGlvbk1lc3NhZ2VzSABSIHBhc3N3b3JkbGVzc05vdGlmaWNhdGlvbk1lc3NhZ2Vz'
|
||||||
|
'EmEKFG1lbW9yaWVzX3VwbG9hZF91cmxzGBIgASgLMi0uc2VydmVyX3RvX2NsaWVudC5SZXNwb2'
|
||||||
|
'5zZS5NZW1vcmllc1VwbG9hZFVybHNIAFISbWVtb3JpZXNVcGxvYWRVcmxzEk4KDW1lbW9yaWVz'
|
||||||
|
'X2xpc3QYEyABKAsyJy5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLk1lbW9yaWVzTGlzdEgAUg'
|
||||||
|
'xtZW1vcmllc0xpc3QSSwoMbWVtb3JpZXNfdXJsGBQgASgLMiYuc2VydmVyX3RvX2NsaWVudC5S'
|
||||||
|
'ZXNwb25zZS5NZW1vcmllc1VybEgAUgttZW1vcmllc1VybBJRCg5tZW1vcmllc191c2FnZRgVIA'
|
||||||
|
'EoCzIoLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuTWVtb3JpZXNVc2FnZUgAUg1tZW1vcmll'
|
||||||
|
'c1VzYWdlQgQKAk9rSgQIBxAISgQICxAMQgoKCFJlc3BvbnNl');
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ class Message_Type extends $pb.ProtobufEnum {
|
||||||
Message_Type._(3, _omitEnumNames ? '' : 'PREKEY_BUNDLE');
|
Message_Type._(3, _omitEnumNames ? '' : 'PREKEY_BUNDLE');
|
||||||
static const Message_Type TEST_NOTIFICATION =
|
static const Message_Type TEST_NOTIFICATION =
|
||||||
Message_Type._(4, _omitEnumNames ? '' : 'TEST_NOTIFICATION');
|
Message_Type._(4, _omitEnumNames ? '' : 'TEST_NOTIFICATION');
|
||||||
|
static const Message_Type CIPHERTEXT_V2 =
|
||||||
|
Message_Type._(5, _omitEnumNames ? '' : 'CIPHERTEXT_V2');
|
||||||
|
|
||||||
static const $core.List<Message_Type> values = <Message_Type>[
|
static const $core.List<Message_Type> values = <Message_Type>[
|
||||||
SENDER_DELIVERY_RECEIPT,
|
SENDER_DELIVERY_RECEIPT,
|
||||||
|
|
@ -32,10 +34,11 @@ class Message_Type extends $pb.ProtobufEnum {
|
||||||
CIPHERTEXT,
|
CIPHERTEXT,
|
||||||
PREKEY_BUNDLE,
|
PREKEY_BUNDLE,
|
||||||
TEST_NOTIFICATION,
|
TEST_NOTIFICATION,
|
||||||
|
CIPHERTEXT_V2,
|
||||||
];
|
];
|
||||||
|
|
||||||
static final $core.List<Message_Type?> _byValue =
|
static final $core.List<Message_Type?> _byValue =
|
||||||
$pb.ProtobufEnum.$_initByValueList(values, 4);
|
$pb.ProtobufEnum.$_initByValueList(values, 5);
|
||||||
static Message_Type? valueOf($core.int value) =>
|
static Message_Type? valueOf($core.int value) =>
|
||||||
value < 0 || value >= _byValue.length ? null : _byValue[value];
|
value < 0 || value >= _byValue.length ? null : _byValue[value];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ const Message_Type$json = {
|
||||||
{'1': 'CIPHERTEXT', '2': 2},
|
{'1': 'CIPHERTEXT', '2': 2},
|
||||||
{'1': 'PREKEY_BUNDLE', '2': 3},
|
{'1': 'PREKEY_BUNDLE', '2': 3},
|
||||||
{'1': 'TEST_NOTIFICATION', '2': 4},
|
{'1': 'TEST_NOTIFICATION', '2': 4},
|
||||||
|
{'1': 'CIPHERTEXT_V2', '2': 5},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -65,10 +66,10 @@ final $typed_data.Uint8List messageDescriptor = $convert.base64Decode(
|
||||||
'CgdNZXNzYWdlEiEKBHR5cGUYASABKA4yDS5NZXNzYWdlLlR5cGVSBHR5cGUSHQoKcmVjZWlwdF'
|
'CgdNZXNzYWdlEiEKBHR5cGUYASABKA4yDS5NZXNzYWdlLlR5cGVSBHR5cGUSHQoKcmVjZWlwdF'
|
||||||
'9pZBgCIAEoCVIJcmVjZWlwdElkEjAKEWVuY3J5cHRlZF9jb250ZW50GAMgASgMSABSEGVuY3J5'
|
'9pZBgCIAEoCVIJcmVjZWlwdElkEjAKEWVuY3J5cHRlZF9jb250ZW50GAMgASgMSABSEGVuY3J5'
|
||||||
'cHRlZENvbnRlbnSIAQESQwoRcGxhaW50ZXh0X2NvbnRlbnQYBCABKAsyES5QbGFpbnRleHRDb2'
|
'cHRlZENvbnRlbnSIAQESQwoRcGxhaW50ZXh0X2NvbnRlbnQYBCABKAsyES5QbGFpbnRleHRDb2'
|
||||||
'50ZW50SAFSEHBsYWludGV4dENvbnRlbnSIAQEidAoEVHlwZRIbChdTRU5ERVJfREVMSVZFUllf'
|
'50ZW50SAFSEHBsYWludGV4dENvbnRlbnSIAQEihwEKBFR5cGUSGwoXU0VOREVSX0RFTElWRVJZ'
|
||||||
'UkVDRUlQVBAAEhUKEVBMQUlOVEVYVF9DT05URU5UEAESDgoKQ0lQSEVSVEVYVBACEhEKDVBSRU'
|
'X1JFQ0VJUFQQABIVChFQTEFJTlRFWFRfQ09OVEVOVBABEg4KCkNJUEhFUlRFWFQQAhIRCg1QUk'
|
||||||
'tFWV9CVU5ETEUQAxIVChFURVNUX05PVElGSUNBVElPThAEQhQKEl9lbmNyeXB0ZWRfY29udGVu'
|
'VLRVlfQlVORExFEAMSFQoRVEVTVF9OT1RJRklDQVRJT04QBBIRCg1DSVBIRVJURVhUX1YyEAVC'
|
||||||
'dEIUChJfcGxhaW50ZXh0X2NvbnRlbnQ=');
|
'FAoSX2VuY3J5cHRlZF9jb250ZW50QhQKEl9wbGFpbnRleHRfY29udGVudA==');
|
||||||
|
|
||||||
@$core.Deprecated('Use plaintextContentDescriptor instead')
|
@$core.Deprecated('Use plaintextContentDescriptor instead')
|
||||||
const PlaintextContent$json = {
|
const PlaintextContent$json = {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ message Message {
|
||||||
CIPHERTEXT = 2;
|
CIPHERTEXT = 2;
|
||||||
PREKEY_BUNDLE = 3;
|
PREKEY_BUNDLE = 3;
|
||||||
TEST_NOTIFICATION = 4;
|
TEST_NOTIFICATION = 4;
|
||||||
|
CIPHERTEXT_V2 = 5;
|
||||||
}
|
}
|
||||||
Type type = 1;
|
Type type = 1;
|
||||||
string receipt_id = 2;
|
string receipt_id = 2;
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,23 @@ import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/services/user.service.dart';
|
import 'package:twonly/src/services/user.service.dart';
|
||||||
|
import 'package:twonly/src/visual/themes/light.dart';
|
||||||
|
|
||||||
class SettingsChangeProvider with ChangeNotifier, DiagnosticableTreeMixin {
|
class SettingsChangeProvider with ChangeNotifier, DiagnosticableTreeMixin {
|
||||||
late ThemeMode _themeMode;
|
late ThemeMode _themeMode;
|
||||||
|
late Color _primaryColor;
|
||||||
|
|
||||||
ThemeMode get themeMode => _themeMode;
|
ThemeMode get themeMode => _themeMode;
|
||||||
|
Color get primaryColor => _primaryColor;
|
||||||
|
|
||||||
void loadSettings() {
|
void loadSettings() {
|
||||||
if (userService.isUserCreated) {
|
if (userService.isUserCreated) {
|
||||||
_themeMode = userService.currentUser.themeMode;
|
_themeMode = userService.currentUser.themeMode;
|
||||||
|
_primaryColor = userService.currentUser.primaryColor;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
} else {
|
} else {
|
||||||
_themeMode = ThemeMode.system;
|
_themeMode = ThemeMode.system;
|
||||||
|
_primaryColor = defaultPrimaryColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,4 +33,16 @@ class SettingsChangeProvider with ChangeNotifier, DiagnosticableTreeMixin {
|
||||||
|
|
||||||
await UserService.update((u) => u.themeMode = newThemeMode);
|
await UserService.update((u) => u.themeMode = newThemeMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> updatePrimaryColor(Color newColor) async {
|
||||||
|
if (newColor.toARGB32() == _primaryColor.toARGB32()) return;
|
||||||
|
|
||||||
|
_primaryColor = newColor;
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
await UserService.update(
|
||||||
|
(u) => u.primaryColorValue = newColor.toARGB32(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ import 'package:twonly/core/bridge/wrapper/key_manager.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pb.dart'
|
||||||
|
as client;
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pbserver.dart';
|
import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pbserver.dart';
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart'
|
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart'
|
||||||
|
|
@ -625,7 +627,13 @@ class ApiService {
|
||||||
|
|
||||||
final signalStore = await getSignalStoreFromIdentity(signalIdentity);
|
final signalStore = await getSignalStoreFromIdentity(signalIdentity);
|
||||||
|
|
||||||
final signedPreKey = (await signalStore.loadSignedPreKeys())[0];
|
final signedPreKeysList = await signalStore.loadSignedPreKeys();
|
||||||
|
if (signedPreKeysList.isEmpty) {
|
||||||
|
throw Exception(
|
||||||
|
'Signal Signed PreKeys list is empty. Database insertion likely failed due to lack of storage space or a corrupted database.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final signedPreKey = signedPreKeysList[0];
|
||||||
|
|
||||||
final loginToken = await RustKeyManager.getLoginToken();
|
final loginToken = await RustKeyManager.getLoginToken();
|
||||||
|
|
||||||
|
|
@ -992,6 +1000,28 @@ class ApiService {
|
||||||
return sendRequestSync(req);
|
return sendRequestSync(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Result> uploadPqcPreKeys(
|
||||||
|
int eccSignedPreKeyId,
|
||||||
|
Uint8List eccSignedPreKey,
|
||||||
|
Uint8List eccSignedPreKeySignature,
|
||||||
|
int kyberSignedPreKeyId,
|
||||||
|
Uint8List kyberSignedPreKey,
|
||||||
|
Uint8List kyberSignedPreKeySignature,
|
||||||
|
List<client.ApplicationData_PqcPreKey> prekeys,
|
||||||
|
) async {
|
||||||
|
final get = ApplicationData_UploadPqcPreKeys()
|
||||||
|
..eccSignedPrekeyId = Int64(eccSignedPreKeyId)
|
||||||
|
..eccSignedPrekey = eccSignedPreKey
|
||||||
|
..eccSignedPrekeySignature = eccSignedPreKeySignature
|
||||||
|
..kyberSignedPrekeyId = Int64(kyberSignedPreKeyId)
|
||||||
|
..kyberSignedPrekey = kyberSignedPreKey
|
||||||
|
..kyberSignedPrekeySignature = kyberSignedPreKeySignature
|
||||||
|
..prekeys.addAll(prekeys);
|
||||||
|
final appData = ApplicationData()..uploadPqcPrekeys = get;
|
||||||
|
final req = createClientToServerFromApplicationData(appData);
|
||||||
|
return sendRequestSync(req);
|
||||||
|
}
|
||||||
|
|
||||||
Future<Response_PlanBallance?> loadPlanBalance({bool useCache = true}) async {
|
Future<Response_PlanBallance?> loadPlanBalance({bool useCache = true}) async {
|
||||||
final ballance = await getPlanBallance();
|
final ballance = await getPlanBallance();
|
||||||
if (ballance != null) {
|
if (ballance != null) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:fixnum/fixnum.dart';
|
import 'package:fixnum/fixnum.dart';
|
||||||
|
import 'package:twonly/core/bridge/wrapper/signal.dart';
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pb.dart'
|
import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pb.dart'
|
||||||
as client;
|
as client;
|
||||||
import 'package:twonly/src/services/signal/identity.signal.dart';
|
import 'package:twonly/src/services/signal/identity.signal.dart';
|
||||||
|
|
@ -18,3 +19,25 @@ Future<client.Response> handleRequestNewPreKey() async {
|
||||||
final ok = client.Response_Ok()..prekeys = prekeys;
|
final ok = client.Response_Ok()..prekeys = prekeys;
|
||||||
return client.Response()..ok = ok;
|
return client.Response()..ok = ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<client.Response?> handleRequestNewPqcPreKey() async {
|
||||||
|
final pqcKeys = await RustSignal.generatePqcPrekeys();
|
||||||
|
if (pqcKeys.isEmpty) return null;
|
||||||
|
|
||||||
|
final prekeysList = <client.ApplicationData_PqcPreKey>[];
|
||||||
|
for (final pqcKey in pqcKeys) {
|
||||||
|
prekeysList.add(
|
||||||
|
client.ApplicationData_PqcPreKey(
|
||||||
|
eccPreKeyId: Int64(pqcKey.eccPreKeyId),
|
||||||
|
eccPreKey: pqcKey.eccPreKey,
|
||||||
|
kyberPreKeyId: Int64(pqcKey.kyberPreKeyId),
|
||||||
|
kyberPreKey: pqcKey.kyberPreKey,
|
||||||
|
kyberPreKeySignature: pqcKey.kyberPreKeySignature,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final prekeys = client.Response_PqcPrekeys(prekeys: prekeysList);
|
||||||
|
final ok = client.Response_Ok()..prekeysPqc = prekeys;
|
||||||
|
return client.Response()..ok = ok;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -392,6 +392,7 @@ Future<void> insertMediaFileInMessagesTable(
|
||||||
List<String> groupIds, {
|
List<String> groupIds, {
|
||||||
AdditionalMessageData? additionalData,
|
AdditionalMessageData? additionalData,
|
||||||
}) async {
|
}) async {
|
||||||
|
await twonlyDB.transaction(() async {
|
||||||
await twonlyDB.mediaFilesDao.updateAllMediaFiles(
|
await twonlyDB.mediaFilesDao.updateAllMediaFiles(
|
||||||
const MediaFilesCompanion(
|
const MediaFilesCompanion(
|
||||||
isDraftMedia: Value(false),
|
isDraftMedia: Value(false),
|
||||||
|
|
@ -418,7 +419,10 @@ Future<void> insertMediaFileInMessagesTable(
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await twonlyDB.groupsDao.increaseLastMessageExchange(groupId, clock.now());
|
await twonlyDB.groupsDao.increaseLastMessageExchange(
|
||||||
|
groupId,
|
||||||
|
clock.now(),
|
||||||
|
);
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
Log.info(
|
Log.info(
|
||||||
'Created message ${message.messageId} for media ${message.mediaId}',
|
'Created message ${message.messageId} for media ${message.mediaId}',
|
||||||
|
|
@ -435,6 +439,7 @@ Future<void> insertMediaFileInMessagesTable(
|
||||||
Log.error('Error inserting media upload message in database.');
|
Log.error('Error inserting media upload message in database.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
unawaited(startBackgroundMediaUpload(mediaService));
|
unawaited(startBackgroundMediaUpload(mediaService));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ import 'dart:io';
|
||||||
import 'package:clock/clock.dart';
|
import 'package:clock/clock.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:fixnum/fixnum.dart';
|
import 'package:fixnum/fixnum.dart';
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
|
||||||
import 'package:mutex/mutex.dart';
|
import 'package:mutex/mutex.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
|
import 'package:twonly/src/database/tables/contacts.table.dart';
|
||||||
import 'package:twonly/src/database/tables/messages.table.dart';
|
import 'package:twonly/src/database/tables/messages.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
||||||
|
|
@ -176,11 +176,11 @@ Future<(Uint8List, Uint8List?)?> _tryToSendCompleteMessageInternal({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.type == pb.Message_Type.CIPHERTEXT) {
|
if (message.type == pb.Message_Type.CIPHERTEXT) {
|
||||||
final cipherText = await signalEncryptMessage(
|
final encryptResult = await signalEncryptMessage(
|
||||||
receipt.contactId,
|
receipt.contactId,
|
||||||
Uint8List.fromList(message.encryptedContent),
|
Uint8List.fromList(message.encryptedContent),
|
||||||
);
|
);
|
||||||
if (cipherText == null) {
|
if (encryptResult == null) {
|
||||||
Log.error(
|
Log.error(
|
||||||
'[${receipt.receiptId}] Could not encrypt the message for user ${receipt.contactId}. Aborting and trying again.',
|
'[${receipt.receiptId}] Could not encrypt the message for user ${receipt.contactId}. Aborting and trying again.',
|
||||||
);
|
);
|
||||||
|
|
@ -194,16 +194,31 @@ Future<(Uint8List, Uint8List?)?> _tryToSendCompleteMessageInternal({
|
||||||
await twonlyDB.receiptsDao.deleteReceipt(receipt.receiptId);
|
await twonlyDB.receiptsDao.deleteReceipt(receipt.receiptId);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
message.encryptedContent = cipherText.serialize();
|
message
|
||||||
switch (cipherText.getType()) {
|
..encryptedContent = encryptResult.ciphertext
|
||||||
case CiphertextMessage.prekeyType:
|
..type = encryptResult.type;
|
||||||
message.type = pb.Message_Type.PREKEY_BUNDLE;
|
} else if (message.type == pb.Message_Type.CIPHERTEXT_V2) {
|
||||||
case CiphertextMessage.whisperType:
|
final encryptResult = await signalEncryptMessageV2(
|
||||||
message.type = pb.Message_Type.CIPHERTEXT;
|
receipt.contactId,
|
||||||
default:
|
Uint8List.fromList(message.encryptedContent),
|
||||||
Log.error('Invalid ciphertext type: ${cipherText.getType()}.');
|
);
|
||||||
|
if (encryptResult == null) {
|
||||||
|
Log.error(
|
||||||
|
'[${receipt.receiptId}] Could not encrypt the message (V2) for user ${receipt.contactId}. Aborting and trying again.',
|
||||||
|
);
|
||||||
|
if (receipt.messageId != null) {
|
||||||
|
await twonlyDB.messagesDao.handleMessageAckByServer(
|
||||||
|
receipt.contactId,
|
||||||
|
receipt.messageId!,
|
||||||
|
clock.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await twonlyDB.receiptsDao.deleteReceipt(receipt.receiptId);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
message
|
||||||
|
..encryptedContent = encryptResult.ciphertext
|
||||||
|
..type = encryptResult.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onlyReturnEncryptedData) {
|
if (onlyReturnEncryptedData) {
|
||||||
|
|
@ -482,8 +497,11 @@ Future<(Uint8List, Uint8List?)?> sendCipherText(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final contact = await twonlyDB.contactsDao.getContactById(contactId);
|
||||||
|
final isV2 = contact?.signalVersion == SignalVersion.v2;
|
||||||
|
|
||||||
final response = pb.Message()
|
final response = pb.Message()
|
||||||
..type = pb.Message_Type.CIPHERTEXT
|
..type = isV2 ? pb.Message_Type.CIPHERTEXT_V2 : pb.Message_Type.CIPHERTEXT
|
||||||
..encryptedContent = encryptedContent.writeToBuffer();
|
..encryptedContent = encryptedContent.writeToBuffer();
|
||||||
|
|
||||||
var retryCounter = 0;
|
var retryCounter = 0;
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ Future<void> handleServerMessage(server.ServerToClient msg) async {
|
||||||
try {
|
try {
|
||||||
if (msg.v0.hasRequestNewPreKeys()) {
|
if (msg.v0.hasRequestNewPreKeys()) {
|
||||||
response = await handleRequestNewPreKey();
|
response = await handleRequestNewPreKey();
|
||||||
|
} else if (msg.v0.hasRequestNewPqcPreKeys()) {
|
||||||
|
response = (await handleRequestNewPqcPreKey()) ?? response;
|
||||||
} else if (msg.v0.hasNewMessage()) {
|
} else if (msg.v0.hasNewMessage()) {
|
||||||
Log.info('Got 1 message from the server.');
|
Log.info('Got 1 message from the server.');
|
||||||
await handleClient2ClientMessage(msg.v0.newMessage);
|
await handleClient2ClientMessage(msg.v0.newMessage);
|
||||||
|
|
@ -190,6 +192,7 @@ Future<void> _handleClient2ClientMessage(
|
||||||
}
|
}
|
||||||
|
|
||||||
case Message_Type.CIPHERTEXT:
|
case Message_Type.CIPHERTEXT:
|
||||||
|
case Message_Type.CIPHERTEXT_V2:
|
||||||
case Message_Type.PREKEY_BUNDLE:
|
case Message_Type.PREKEY_BUNDLE:
|
||||||
if (message.hasEncryptedContent()) {
|
if (message.hasEncryptedContent()) {
|
||||||
Value<String>? receiptIdDB;
|
Value<String>? receiptIdDB;
|
||||||
|
|
@ -297,12 +300,23 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessageRaw(
|
||||||
Set<int>? brokenSessionsInCurrentBatch,
|
Set<int>? brokenSessionsInCurrentBatch,
|
||||||
}) async {
|
}) async {
|
||||||
Log.info('[$receiptId] calling signalDecryptMessage');
|
Log.info('[$receiptId] calling signalDecryptMessage');
|
||||||
var (encryptedContent, decryptionErrorType) = await signalDecryptMessage(
|
EncryptedContent? encryptedContent;
|
||||||
|
PlaintextContent_DecryptionErrorMessage_Type? decryptionErrorType;
|
||||||
|
|
||||||
|
if (messageType == Message_Type.CIPHERTEXT_V2) {
|
||||||
|
(encryptedContent, decryptionErrorType) = await signalDecryptMessageV2(
|
||||||
|
fromUserId,
|
||||||
|
encryptedContentRaw,
|
||||||
|
brokenSessionsInCurrentBatch: brokenSessionsInCurrentBatch,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
(encryptedContent, decryptionErrorType) = await signalDecryptMessageV1(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
encryptedContentRaw,
|
encryptedContentRaw,
|
||||||
messageType.value,
|
messageType.value,
|
||||||
brokenSessionsInCurrentBatch: brokenSessionsInCurrentBatch,
|
brokenSessionsInCurrentBatch: brokenSessionsInCurrentBatch,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (encryptedContent == null) {
|
if (encryptedContent == null) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,12 @@ class Result<T, E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTime fromTimestamp(Int64 timeStamp) {
|
DateTime fromTimestamp(Int64 timeStamp) {
|
||||||
return DateTime.fromMillisecondsSinceEpoch(timeStamp.toInt());
|
final date = DateTime.fromMillisecondsSinceEpoch(timeStamp.toInt());
|
||||||
|
final now = DateTime.now();
|
||||||
|
if (date.isAfter(now)) {
|
||||||
|
return now;
|
||||||
|
}
|
||||||
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore: strict_raw_type
|
// ignore: strict_raw_type
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,10 @@ Future<bool> initBackgroundExecution() async {
|
||||||
final Mutex _keyValueMutex = Mutex();
|
final Mutex _keyValueMutex = Mutex();
|
||||||
|
|
||||||
// ignore: unreachable_from_main
|
// ignore: unreachable_from_main
|
||||||
Future<void> handlePeriodicTask({int lastExecutionInSecondsLimit = 120}) async {
|
Future<bool> backgroundFetch({
|
||||||
|
int? lastExecutionInSecondsLimit = 120,
|
||||||
|
}) async {
|
||||||
|
if (lastExecutionInSecondsLimit != null) {
|
||||||
final shouldBeExecuted = await exclusiveAccess(
|
final shouldBeExecuted = await exclusiveAccess(
|
||||||
lockName: 'periodic_task',
|
lockName: 'periodic_task',
|
||||||
mutex: _keyValueMutex,
|
mutex: _keyValueMutex,
|
||||||
|
|
@ -116,9 +119,10 @@ Future<void> handlePeriodicTask({int lastExecutionInSecondsLimit = 120}) async {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!shouldBeExecuted) return;
|
if (!shouldBeExecuted) return false;
|
||||||
|
}
|
||||||
|
|
||||||
Log.info('eu.twonly.periodic_task was called.');
|
Log.info('Periodic task was called.');
|
||||||
AppState.gotMessageFromServer = false;
|
AppState.gotMessageFromServer = false;
|
||||||
|
|
||||||
final stopwatch = Stopwatch()..start();
|
final stopwatch = Stopwatch()..start();
|
||||||
|
|
@ -130,14 +134,16 @@ Future<void> handlePeriodicTask({int lastExecutionInSecondsLimit = 120}) async {
|
||||||
|
|
||||||
if (!await apiService.connect()) {
|
if (!await apiService.connect()) {
|
||||||
Log.info('Could not connect to the api. Returning early.');
|
Log.info('Could not connect to the api. Returning early.');
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!apiService.isAuthenticated) {
|
if (!apiService.isAuthenticated) {
|
||||||
Log.info('Api is not authenticated. Returning early.');
|
Log.info('Api is not authenticated. Returning early.');
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var receiveMessage = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (!AppState.gotMessageFromServer) {
|
while (!AppState.gotMessageFromServer) {
|
||||||
if (stopwatch.elapsed.inSeconds >= 15) {
|
if (stopwatch.elapsed.inSeconds >= 15) {
|
||||||
|
|
@ -148,19 +154,22 @@ Future<void> handlePeriodicTask({int lastExecutionInSecondsLimit = 120}) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AppState.gotMessageFromServer) {
|
if (AppState.gotMessageFromServer) {
|
||||||
|
receiveMessage = true;
|
||||||
Log.info('Received a server message from the server.');
|
Log.info('Received a server message from the server.');
|
||||||
}
|
}
|
||||||
|
|
||||||
await finishStartedPreprocessing();
|
await finishStartedPreprocessing();
|
||||||
|
|
||||||
|
if (lastExecutionInSecondsLimit != null) {
|
||||||
await Future.delayed(const Duration(milliseconds: 2000));
|
await Future.delayed(const Duration(milliseconds: 2000));
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
await apiService.close(() {});
|
await apiService.close(() {});
|
||||||
stopwatch.stop();
|
stopwatch.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.info('eu.twonly.periodic_task finished after ${stopwatch.elapsed}.');
|
Log.info('Periodic task finished after ${stopwatch.elapsed}.');
|
||||||
return;
|
return receiveMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> handleProcessingTask() async {
|
Future<void> handleProcessingTask() async {
|
||||||
|
|
|
||||||
|
|
@ -323,10 +323,12 @@ class MediaFileService {
|
||||||
name: mediaFile.mediaId,
|
name: mediaFile.mediaId,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await saveImageToGallery(
|
unawaited(
|
||||||
storedPath.readAsBytesSync(),
|
saveImageToGallery(
|
||||||
|
await storedPath.readAsBytes(),
|
||||||
createdAt: mediaFile.createdAt,
|
createdAt: mediaFile.createdAt,
|
||||||
name: mediaFile.mediaId,
|
name: mediaFile.mediaId,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
|
import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
|
||||||
import 'package:cryptography_plus/cryptography_plus.dart';
|
import 'package:cryptography_plus/cryptography_plus.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
|
|
@ -100,6 +102,7 @@ Future<void> showLocalPushNotification(
|
||||||
PushUser pushUser,
|
PushUser pushUser,
|
||||||
PushNotification pushNotification, {
|
PushNotification pushNotification, {
|
||||||
String? groupId,
|
String? groupId,
|
||||||
|
String? titleSuffix,
|
||||||
}) async {
|
}) async {
|
||||||
String? title;
|
String? title;
|
||||||
String? body;
|
String? body;
|
||||||
|
|
@ -125,7 +128,9 @@ Future<void> showLocalPushNotification(
|
||||||
if (targetGroupId != null) {
|
if (targetGroupId != null) {
|
||||||
try {
|
try {
|
||||||
final currentUri = routerProvider.routerDelegate.currentConfiguration.uri;
|
final currentUri = routerProvider.routerDelegate.currentConfiguration.uri;
|
||||||
if (currentUri.path.contains(targetGroupId)) {
|
final isResumed =
|
||||||
|
WidgetsBinding.instance.lifecycleState == AppLifecycleState.resumed;
|
||||||
|
if (isResumed && currentUri.path.contains(targetGroupId)) {
|
||||||
Log.info(
|
Log.info(
|
||||||
'Suppressing local push notification because chat with group $targetGroupId is currently open.',
|
'Suppressing local push notification because chat with group $targetGroupId is currently open.',
|
||||||
);
|
);
|
||||||
|
|
@ -137,6 +142,10 @@ Future<void> showLocalPushNotification(
|
||||||
}
|
}
|
||||||
|
|
||||||
title = pushUser.displayName;
|
title = pushUser.displayName;
|
||||||
|
if (titleSuffix != null && titleSuffix.isNotEmpty) {
|
||||||
|
title = '$title $titleSuffix';
|
||||||
|
}
|
||||||
|
|
||||||
body = getPushNotificationText(pushNotification);
|
body = getPushNotificationText(pushNotification);
|
||||||
if (body == '') {
|
if (body == '') {
|
||||||
Log.error('No push notification type defined!');
|
Log.error('No push notification type defined!');
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||||
|
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
if (isInitialized) {
|
if (isInitialized) {
|
||||||
await handlePeriodicTask(lastExecutionInSecondsLimit: 10);
|
await backgroundFetch(lastExecutionInSecondsLimit: 3);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// make sure every thing run...
|
// make sure every thing run...
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/constants/secure_storage.keys.dart';
|
import 'package:twonly/src/constants/secure_storage.keys.dart';
|
||||||
|
import 'package:twonly/src/services/background/callback_dispatcher.background.dart';
|
||||||
import 'package:twonly/src/services/notifications/background.notifications.dart';
|
import 'package:twonly/src/services/notifications/background.notifications.dart';
|
||||||
import 'package:twonly/src/services/notifications/fcm.background.dart';
|
import 'package:twonly/src/services/notifications/fcm.background.dart';
|
||||||
import 'package:twonly/src/services/user.service.dart';
|
import 'package:twonly/src/services/user.service.dart';
|
||||||
|
|
@ -144,6 +145,7 @@ class FcmNotificationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> handleRemoteMessage(RemoteMessage message) async {
|
static Future<void> handleRemoteMessage(RemoteMessage message) async {
|
||||||
|
Log.info('handleRemoteMessage received message: ${message.messageId}');
|
||||||
await _updateLastFcmMessageTimestamp();
|
await _updateLastFcmMessageTimestamp();
|
||||||
if (!Platform.isAndroid) {
|
if (!Platform.isAndroid) {
|
||||||
Log.error('Got message in Dart while on iOS');
|
Log.error('Got message in Dart while on iOS');
|
||||||
|
|
@ -155,6 +157,24 @@ class FcmNotificationService {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In scenarios like Android Doze Mode or aggressive background restrictions, the OS may kill
|
||||||
|
// or heavily restrict network access, preventing the WebSocket from connecting in time.
|
||||||
|
// By parsing the FCM data payload offline, we can instantly display the notification, which also
|
||||||
|
// prevents FCM from penalizing/downgrading the app's data message priority for failing to show a notification.
|
||||||
|
// This is just a workarround until the new Rust decryption is enrolled fully.
|
||||||
|
final pushDataString = message.data['push_data'] as String?;
|
||||||
|
if (pushDataString != null) {
|
||||||
|
if (apiService.isConnected) {
|
||||||
|
Log.info('Got FCM message, but API is connected...');
|
||||||
|
} else {
|
||||||
|
Log.info('Trying to connect to the API in the background.');
|
||||||
|
|
||||||
|
if (await backgroundFetch()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (message.notification != null || message.data['title'] != null) {
|
if (message.notification != null || message.data['title'] != null) {
|
||||||
final title =
|
final title =
|
||||||
message.notification?.title ?? message.data['title'] as String? ?? '';
|
message.notification?.title ?? message.data['title'] as String? ?? '';
|
||||||
|
|
|
||||||
|
|
@ -3,23 +3,48 @@ import 'dart:typed_data';
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
// ignore: implementation_imports
|
// ignore: implementation_imports
|
||||||
import 'package:libsignal_protocol_dart/src/invalid_message_exception.dart';
|
import 'package:libsignal_protocol_dart/src/invalid_message_exception.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
import 'package:twonly/core/bridge/wrapper/signal.dart';
|
||||||
|
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'
|
||||||
|
as pb;
|
||||||
import 'package:twonly/src/services/api/messages.api.dart';
|
import 'package:twonly/src/services/api/messages.api.dart';
|
||||||
import 'package:twonly/src/services/signal/protocol_state.signal.dart';
|
import 'package:twonly/src/services/signal/protocol_state.signal.dart';
|
||||||
import 'package:twonly/src/services/signal/session.signal.dart';
|
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||||
import 'package:twonly/src/services/signal/utils.signal.dart';
|
import 'package:twonly/src/services/signal/utils.signal.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
Future<CiphertextMessage?> signalEncryptMessage(
|
class SignalEncryptResult {
|
||||||
|
SignalEncryptResult(this.ciphertext, this.type);
|
||||||
|
final Uint8List ciphertext;
|
||||||
|
final pb.Message_Type type;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SignalEncryptResult?> signalEncryptMessage(
|
||||||
int target,
|
int target,
|
||||||
Uint8List plaintextContent,
|
Uint8List plaintextContent,
|
||||||
) async {
|
) async {
|
||||||
return lockingSignalProtocol.protect<CiphertextMessage?>(() async {
|
return lockingSignalProtocol.protect<SignalEncryptResult?>(() async {
|
||||||
return _signalEncryptMessage(target, plaintextContent);
|
return _signalEncryptMessageV1(target, plaintextContent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<CiphertextMessage?> _signalEncryptMessage(
|
Future<SignalEncryptResult?> signalEncryptMessageV2(
|
||||||
|
int target,
|
||||||
|
Uint8List plaintextContent,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
final res = await RustSignal.encrypt(
|
||||||
|
name: target.toString(),
|
||||||
|
deviceId: 1,
|
||||||
|
plaintext: plaintextContent,
|
||||||
|
);
|
||||||
|
return SignalEncryptResult(res, pb.Message_Type.CIPHERTEXT_V2);
|
||||||
|
} catch (e) {
|
||||||
|
Log.error('Could not encrypt message (V2) for target $target: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SignalEncryptResult?> _signalEncryptMessageV1(
|
||||||
int target,
|
int target,
|
||||||
Uint8List plaintextContent,
|
Uint8List plaintextContent,
|
||||||
) async {
|
) async {
|
||||||
|
|
@ -27,28 +52,93 @@ Future<CiphertextMessage?> _signalEncryptMessage(
|
||||||
final signalStore = (await getSignalStore())!;
|
final signalStore = (await getSignalStore())!;
|
||||||
final address = getSignalAddress(target);
|
final address = getSignalAddress(target);
|
||||||
final session = SessionCipher.fromStore(signalStore, address);
|
final session = SessionCipher.fromStore(signalStore, address);
|
||||||
return await session.encrypt(plaintextContent);
|
final cipherText = await session.encrypt(plaintextContent);
|
||||||
|
|
||||||
|
pb.Message_Type type;
|
||||||
|
switch (cipherText.getType()) {
|
||||||
|
case CiphertextMessage.prekeyType:
|
||||||
|
type = pb.Message_Type.PREKEY_BUNDLE;
|
||||||
|
case CiphertextMessage.whisperType:
|
||||||
|
type = pb.Message_Type.CIPHERTEXT;
|
||||||
|
default:
|
||||||
|
Log.error('Invalid ciphertext type: ${cipherText.getType()}.');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SignalEncryptResult(cipherText.serialize(), type);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.error('Could not encrypt message for target $target: $e');
|
Log.error('Could not encrypt message (V1) for target $target: $e');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<(EncryptedContent?, PlaintextContent_DecryptionErrorMessage_Type?)>
|
Future<(pb.EncryptedContent?, pb.PlaintextContent_DecryptionErrorMessage_Type?)>
|
||||||
signalDecryptMessage(
|
signalDecryptMessageV2(
|
||||||
int fromUserId,
|
int fromUserId,
|
||||||
Uint8List encryptedContentRaw,
|
Uint8List encryptedContentRaw, {
|
||||||
int type, {
|
|
||||||
Set<int>? brokenSessionsInCurrentBatch,
|
Set<int>? brokenSessionsInCurrentBatch,
|
||||||
}) async {
|
}) async {
|
||||||
// Hold the lock only for the cryptographic operation, not for network I/O
|
Log.info('Acquiring lockingSignalProtocol for $fromUserId (V2)');
|
||||||
Log.info('Acquiring lockingSignalProtocol for $fromUserId');
|
|
||||||
final (
|
final (
|
||||||
decryptedContent,
|
decryptedContent,
|
||||||
errorType,
|
errorType,
|
||||||
needsResync,
|
needsResync,
|
||||||
) = await lockingSignalProtocol.protect(() async {
|
) = await lockingSignalProtocol.protect(() async {
|
||||||
Log.info('Lock acquired for $fromUserId');
|
Log.info('Lock acquired for $fromUserId (V2)');
|
||||||
|
try {
|
||||||
|
final plaintext = await RustSignal.decrypt(
|
||||||
|
name: fromUserId.toString(),
|
||||||
|
deviceId: 1,
|
||||||
|
ciphertext: encryptedContentRaw,
|
||||||
|
);
|
||||||
|
recordResyncAttempt(fromUserId, success: true);
|
||||||
|
return (pb.EncryptedContent.fromBuffer(plaintext), null, false);
|
||||||
|
} catch (e) {
|
||||||
|
Log.error('Could not decrypt message (V2) from $fromUserId: $e');
|
||||||
|
return (
|
||||||
|
null,
|
||||||
|
pb.PlaintextContent_DecryptionErrorMessage_Type.UNKNOWN,
|
||||||
|
true, // Needs resync on failure
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Log.info('Released lockingSignalProtocol for $fromUserId (V2)');
|
||||||
|
|
||||||
|
if (needsResync) {
|
||||||
|
brokenSessionsInCurrentBatch?.add(fromUserId);
|
||||||
|
if (shouldAttemptResync(fromUserId)) {
|
||||||
|
if (await handleSessionResync(fromUserId)) {
|
||||||
|
recordResyncAttempt(fromUserId, success: false);
|
||||||
|
await sendCipherText(
|
||||||
|
fromUserId,
|
||||||
|
pb.EncryptedContent(
|
||||||
|
errorMessages: pb.EncryptedContent_ErrorMessages(
|
||||||
|
type: pb.EncryptedContent_ErrorMessages_Type.SESSION_OUT_OF_SYNC,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (decryptedContent, errorType);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<(pb.EncryptedContent?, pb.PlaintextContent_DecryptionErrorMessage_Type?)>
|
||||||
|
signalDecryptMessageV1(
|
||||||
|
int fromUserId,
|
||||||
|
Uint8List encryptedContentRaw,
|
||||||
|
int type, {
|
||||||
|
Set<int>? brokenSessionsInCurrentBatch,
|
||||||
|
}) async {
|
||||||
|
Log.info('Acquiring lockingSignalProtocol for $fromUserId (V1)');
|
||||||
|
final (
|
||||||
|
decryptedContent,
|
||||||
|
errorType,
|
||||||
|
needsResync,
|
||||||
|
) = await lockingSignalProtocol.protect(() async {
|
||||||
|
Log.info('Lock acquired for $fromUserId (V1)');
|
||||||
try {
|
try {
|
||||||
final session = SessionCipher.fromStore(
|
final session = SessionCipher.fromStore(
|
||||||
(await getSignalStore())!,
|
(await getSignalStore())!,
|
||||||
|
|
@ -70,18 +160,18 @@ signalDecryptMessage(
|
||||||
Log.error('Unknown Message Decryption Type: $type');
|
Log.error('Unknown Message Decryption Type: $type');
|
||||||
return (
|
return (
|
||||||
null,
|
null,
|
||||||
PlaintextContent_DecryptionErrorMessage_Type.UNKNOWN,
|
pb.PlaintextContent_DecryptionErrorMessage_Type.UNKNOWN,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
recordResyncAttempt(fromUserId, success: true);
|
recordResyncAttempt(fromUserId, success: true);
|
||||||
return (EncryptedContent.fromBuffer(plaintext), null, false);
|
return (pb.EncryptedContent.fromBuffer(plaintext), null, false);
|
||||||
} on InvalidKeyIdException catch (e) {
|
} on InvalidKeyIdException catch (e) {
|
||||||
Log.warn(e);
|
Log.warn(e);
|
||||||
return (
|
return (
|
||||||
null,
|
null,
|
||||||
PlaintextContent_DecryptionErrorMessage_Type.PREKEY_UNKNOWN,
|
pb.PlaintextContent_DecryptionErrorMessage_Type.PREKEY_UNKNOWN,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
} on DuplicateMessageException catch (e) {
|
} on DuplicateMessageException catch (e) {
|
||||||
|
|
@ -91,27 +181,27 @@ signalDecryptMessage(
|
||||||
Log.info(e);
|
Log.info(e);
|
||||||
return (
|
return (
|
||||||
null,
|
null,
|
||||||
PlaintextContent_DecryptionErrorMessage_Type.UNKNOWN,
|
pb.PlaintextContent_DecryptionErrorMessage_Type.UNKNOWN,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
} on InvalidMessageException catch (e) {
|
} on InvalidMessageException catch (e) {
|
||||||
Log.warn(e);
|
Log.warn(e);
|
||||||
return (
|
return (
|
||||||
null,
|
null,
|
||||||
PlaintextContent_DecryptionErrorMessage_Type.UNKNOWN,
|
pb.PlaintextContent_DecryptionErrorMessage_Type.UNKNOWN,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.error(e);
|
Log.error(e);
|
||||||
return (
|
return (
|
||||||
null,
|
null,
|
||||||
PlaintextContent_DecryptionErrorMessage_Type.UNKNOWN,
|
pb.PlaintextContent_DecryptionErrorMessage_Type.UNKNOWN,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Log.info('Released lockingSignalProtocol for $fromUserId');
|
Log.info('Released lockingSignalProtocol for $fromUserId (V1)');
|
||||||
|
|
||||||
// Handle session resync OUTSIDE the lock to avoid holding it during
|
// Handle session resync OUTSIDE the lock to avoid holding it during
|
||||||
// network round-trips (which can block for up to 60 seconds)
|
// network round-trips (which can block for up to 60 seconds)
|
||||||
|
|
@ -127,9 +217,9 @@ signalDecryptMessage(
|
||||||
// session
|
// session
|
||||||
await sendCipherText(
|
await sendCipherText(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
EncryptedContent(
|
pb.EncryptedContent(
|
||||||
errorMessages: EncryptedContent_ErrorMessages(
|
errorMessages: pb.EncryptedContent_ErrorMessages(
|
||||||
type: EncryptedContent_ErrorMessages_Type.SESSION_OUT_OF_SYNC,
|
type: pb.EncryptedContent_ErrorMessages_Type.SESSION_OUT_OF_SYNC,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:clock/clock.dart';
|
import 'package:clock/clock.dart';
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
import 'package:twonly/core/bridge/wrapper/key_manager.dart';
|
import 'package:twonly/core/bridge/wrapper/key_manager.dart';
|
||||||
|
import 'package:twonly/core/bridge/wrapper/signal.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/database/signal/signal_signed_pre_key_store.dart';
|
import 'package:twonly/src/database/signal/signal_signed_pre_key_store.dart';
|
||||||
import 'package:twonly/src/model/json/signal_identity.model.dart';
|
import 'package:twonly/src/model/json/signal_identity.model.dart';
|
||||||
|
|
@ -13,26 +15,20 @@ import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
||||||
class SignalIdentityService {
|
class SignalIdentityService {
|
||||||
static Future<void> onAuthenticated() async {
|
static Future<void> onAuthenticated() async {
|
||||||
if (userService.currentUser.signalLastSignedPreKeyUpdated != null) {
|
final now = clock.now();
|
||||||
final fortyEightHoursAgo = clock.now().subtract(
|
final fortyEightHoursAgo = now.subtract(const Duration(hours: 48));
|
||||||
const Duration(hours: 48),
|
final oneWeekAgo = now.subtract(const Duration(days: 7));
|
||||||
);
|
|
||||||
final isYoungerThan48Hours =
|
if (userService.currentUser.signalLastSignedPreKeyUpdated == null ||
|
||||||
(userService.currentUser.signalLastSignedPreKeyUpdated!).isAfter(
|
!userService.currentUser.signalLastSignedPreKeyUpdated!.isAfter(
|
||||||
fortyEightHoursAgo,
|
fortyEightHoursAgo,
|
||||||
);
|
)) {
|
||||||
if (isYoungerThan48Hours) {
|
|
||||||
// The key does live for 48 hours then it expires and a new key is generated.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final signedPreKey = await _getNewSignalSignedPreKey();
|
final signedPreKey = await _getNewSignalSignedPreKey();
|
||||||
if (signedPreKey == null) {
|
if (signedPreKey == null) {
|
||||||
Log.error('could not generate a new signed pre key!');
|
Log.error('could not generate a new signed pre key!');
|
||||||
return;
|
} else {
|
||||||
}
|
|
||||||
await UserService.update((user) {
|
await UserService.update((user) {
|
||||||
user.signalLastSignedPreKeyUpdated = clock.now();
|
user.signalLastSignedPreKeyUpdated = now;
|
||||||
});
|
});
|
||||||
final res = await apiService.updateSignedPreKey(
|
final res = await apiService.updateSignedPreKey(
|
||||||
signedPreKey.id,
|
signedPreKey.id,
|
||||||
|
|
@ -50,6 +46,34 @@ class SignalIdentityService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (userService.currentUser.signalLastPqcPreKeysUploaded == null ||
|
||||||
|
!userService.currentUser.signalLastPqcPreKeysUploaded!.isAfter(
|
||||||
|
oneWeekAgo,
|
||||||
|
)) {
|
||||||
|
final bundle = await RustSignal.generateBundle();
|
||||||
|
|
||||||
|
final pqcRes = await apiService.uploadPqcPreKeys(
|
||||||
|
bundle.signedPreKeyId,
|
||||||
|
bundle.signedPreKeyPublic,
|
||||||
|
bundle.signedPreKeySignature,
|
||||||
|
bundle.kyberPreKeyId,
|
||||||
|
bundle.kyberPreKeyPublic,
|
||||||
|
bundle.kyberPreKeySignature,
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pqcRes.isError) {
|
||||||
|
Log.warn('could not update the pqc signed pre key: ${pqcRes.error}');
|
||||||
|
} else {
|
||||||
|
Log.info('updated pqc signed pre key');
|
||||||
|
await UserService.update((user) {
|
||||||
|
user.signalLastPqcPreKeysUploaded = now;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<List<PreKeyRecord>> signalGetPreKeys() async {
|
Future<List<PreKeyRecord>> signalGetPreKeys() async {
|
||||||
return lockingSignalProtocol.protect(() async {
|
return lockingSignalProtocol.protect(() async {
|
||||||
final start = userService.currentUser.currentPreKeyIndexStart;
|
final start = userService.currentUser.currentPreKeyIndexStart;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:drift/drift.dart' as drift;
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
|
import 'package:twonly/core/bridge/wrapper/signal.dart';
|
||||||
|
import 'package:twonly/core/signal/engine.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
|
import 'package:twonly/src/database/tables/contacts.table.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart';
|
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart';
|
||||||
import 'package:twonly/src/services/signal/consts.signal.dart';
|
import 'package:twonly/src/services/signal/consts.signal.dart';
|
||||||
import 'package:twonly/src/services/signal/protocol_state.signal.dart';
|
import 'package:twonly/src/services/signal/protocol_state.signal.dart';
|
||||||
|
|
@ -15,6 +20,106 @@ Future<bool> processSignalUserData(Response_UserData userData) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _processSignalUserData(Response_UserData userData) async {
|
Future<bool> _processSignalUserData(Response_UserData userData) async {
|
||||||
|
if (userData.hasPqcBundle()) {
|
||||||
|
return _processSignalUserDataV2(userData);
|
||||||
|
}
|
||||||
|
return _processSignalUserDataV1(userData);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _processSignalUserDataV2(Response_UserData userData) async {
|
||||||
|
try {
|
||||||
|
final tempIdentityKey = IdentityKey(
|
||||||
|
Curve.decodePoint(
|
||||||
|
DjbECPublicKey(
|
||||||
|
Uint8List.fromList(userData.publicIdentityKey),
|
||||||
|
).serialize(),
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final signalStore = await getSignalStore();
|
||||||
|
if (signalStore != null) {
|
||||||
|
final existingIdentity = await signalStore.getIdentity(
|
||||||
|
SignalProtocolAddress(userData.userId.toString(), defaultDeviceId),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existingIdentity != null && existingIdentity != tempIdentityKey) {
|
||||||
|
Log.error(
|
||||||
|
'Identity key mismatch for contact ${userData.userId}! Existing V1 key does not match the incoming V2 identity key.',
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int? tempEccPreKeyId;
|
||||||
|
Uint8List? tempEccPreKeyPublic;
|
||||||
|
if (userData.pqcBundle.hasPrekey()) {
|
||||||
|
tempEccPreKeyId = userData.pqcBundle.prekey.eccPreKeyId.toInt();
|
||||||
|
tempEccPreKeyPublic = Uint8List.fromList(
|
||||||
|
userData.pqcBundle.prekey.eccPreKey,
|
||||||
|
);
|
||||||
|
} else if (userData.prekeys.isNotEmpty) {
|
||||||
|
tempEccPreKeyId = userData.prekeys.first.id.toInt();
|
||||||
|
tempEccPreKeyPublic = Curve.decodePoint(
|
||||||
|
DjbECPublicKey(
|
||||||
|
Uint8List.fromList(userData.prekeys.first.prekey),
|
||||||
|
).serialize(),
|
||||||
|
1,
|
||||||
|
).serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
final tempKyberPreKeyId = userData.pqcBundle.hasPrekey()
|
||||||
|
? userData.pqcBundle.prekey.kyberPreKeyId.toInt()
|
||||||
|
: userData.pqcBundle.kyberSignedPrekeyId.toInt();
|
||||||
|
final tempKyberPreKeyPublic = userData.pqcBundle.hasPrekey()
|
||||||
|
? Uint8List.fromList(userData.pqcBundle.prekey.kyberPreKey)
|
||||||
|
: Uint8List.fromList(userData.pqcBundle.kyberSignedPrekey);
|
||||||
|
final tempKyberPreKeySignature = userData.pqcBundle.hasPrekey()
|
||||||
|
? Uint8List.fromList(userData.pqcBundle.prekey.kyberPreKeySignature)
|
||||||
|
: Uint8List.fromList(userData.pqcBundle.kyberSignedPrekeySignature);
|
||||||
|
|
||||||
|
final rustBundle = FrbPreKeyBundle(
|
||||||
|
registrationId: userData.registrationId.toInt(),
|
||||||
|
deviceId: 1,
|
||||||
|
preKeyId: tempEccPreKeyId,
|
||||||
|
preKeyPublic: tempEccPreKeyPublic,
|
||||||
|
signedPreKeyId: userData.pqcBundle.eccSignedPrekeyId.toInt(),
|
||||||
|
signedPreKeyPublic: Uint8List.fromList(
|
||||||
|
userData.pqcBundle.eccSignedPrekey,
|
||||||
|
),
|
||||||
|
signedPreKeySignature: Uint8List.fromList(
|
||||||
|
userData.pqcBundle.eccSignedPrekeySignature,
|
||||||
|
),
|
||||||
|
identityKey: tempIdentityKey.publicKey.serialize(),
|
||||||
|
kyberPreKeyId: tempKyberPreKeyId,
|
||||||
|
kyberPreKeyPublic: tempKyberPreKeyPublic,
|
||||||
|
kyberPreKeySignature: tempKyberPreKeySignature,
|
||||||
|
);
|
||||||
|
|
||||||
|
await RustSignal.processPrekeyBundle(
|
||||||
|
name: userData.userId.toString(),
|
||||||
|
deviceId: 1,
|
||||||
|
bundle: rustBundle,
|
||||||
|
);
|
||||||
|
|
||||||
|
final contact = await twonlyDB.contactsDao.getContactById(
|
||||||
|
userData.userId.toInt(),
|
||||||
|
);
|
||||||
|
if (contact != null && contact.signalVersion != SignalVersion.v2) {
|
||||||
|
await twonlyDB.contactsDao.updateContact(
|
||||||
|
contact.userId,
|
||||||
|
const ContactsCompanion(signalVersion: drift.Value(SignalVersion.v2)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
Log.error('could not process pqc bundle: $e');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _processSignalUserDataV1(Response_UserData userData) async {
|
||||||
final SignalProtocolStore? signalStore = await getSignalStore();
|
final SignalProtocolStore? signalStore = await getSignalStore();
|
||||||
|
|
||||||
if (signalStore == null) {
|
if (signalStore == null) {
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,10 @@ Future<bool> authenticateUser(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Catch unexpected platform exceptions (e.g. PlatformException) that
|
||||||
|
// would otherwise propagate and leave callers in a broken state.
|
||||||
|
Log.error('Unexpected authentication error: $e');
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -260,6 +264,28 @@ String formatDateTime(BuildContext context, DateTime? dateTime) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String formatRelativeDateTime(BuildContext context, DateTime? dateTime) {
|
||||||
|
if (dateTime == null) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
final now = clock.now();
|
||||||
|
final today = DateTime(now.year, now.month, now.day);
|
||||||
|
final yesterday = today.subtract(const Duration(days: 1));
|
||||||
|
final dateDay = DateTime(dateTime.year, dateTime.month, dateTime.day);
|
||||||
|
|
||||||
|
final time = DateFormat.Hm(
|
||||||
|
Localizations.localeOf(context).toLanguageTag(),
|
||||||
|
).format(dateTime);
|
||||||
|
|
||||||
|
if (dateDay == today) {
|
||||||
|
return context.lang.todayAt(time);
|
||||||
|
} else if (dateDay == yesterday) {
|
||||||
|
return context.lang.yesterdayAt(time);
|
||||||
|
} else {
|
||||||
|
return formatDateTime(context, dateTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String formatBytes(int bytes) {
|
String formatBytes(int bytes) {
|
||||||
if (bytes <= 0) return '0 Bytes';
|
if (bytes <= 0) return '0 Bytes';
|
||||||
const units = <String>['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
const units = <String>['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,27 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:twonly/locator.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/utils/secure_storage.dart';
|
import 'package:twonly/src/utils/secure_storage.dart';
|
||||||
|
|
||||||
Future<bool> deleteLocalUserData() async {
|
Future<bool> deleteLocalUserData() async {
|
||||||
|
await twonlyDB.close();
|
||||||
|
// Wait for the background drift isolate to potentially shut down
|
||||||
|
await Future.delayed(const Duration(milliseconds: 200));
|
||||||
|
|
||||||
|
// Remove port mappings to prevent connecting to an old, detached isolate that holds a deleted DB file.
|
||||||
|
IsolateNameServer.removePortNameMapping('drift-db/twonly');
|
||||||
|
IsolateNameServer.removePortNameMapping('drift-db/twonly/control');
|
||||||
|
|
||||||
final appDir = await getApplicationSupportDirectory();
|
final appDir = await getApplicationSupportDirectory();
|
||||||
if (appDir.existsSync()) {
|
if (appDir.existsSync()) {
|
||||||
appDir.deleteSync(recursive: true);
|
appDir.deleteSync(recursive: true);
|
||||||
}
|
}
|
||||||
await SecureStorage.instance.deleteAll();
|
await SecureStorage.instance.deleteAll();
|
||||||
|
locator
|
||||||
|
..unregister<TwonlyDB>()
|
||||||
|
..registerLazySingleton<TwonlyDB>(TwonlyDB.new);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
144
lib/src/visual/components/contact_labels.comp.dart
Normal file
144
lib/src/visual/components/contact_labels.comp.dart
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/locator.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
|
class ContactLabels extends StatefulWidget {
|
||||||
|
const ContactLabels({
|
||||||
|
required this.contactId,
|
||||||
|
this.fontSize = 8,
|
||||||
|
this.padding = const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
|
this.emptyText,
|
||||||
|
this.showEmptyText = false,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int contactId;
|
||||||
|
final double fontSize;
|
||||||
|
final EdgeInsetsGeometry padding;
|
||||||
|
final String? emptyText;
|
||||||
|
final bool showEmptyText;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ContactLabels> createState() => _ContactLabelsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ContactLabelsState extends State<ContactLabels> {
|
||||||
|
List<Label> _labels = [];
|
||||||
|
late StreamSubscription<List<Label>> _sub;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_sub = twonlyDB.labelsDao.watchContactLabels(widget.contactId).listen((
|
||||||
|
labels,
|
||||||
|
) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_labels = labels;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_sub.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (_labels.isEmpty) {
|
||||||
|
if (!widget.showEmptyText && widget.emptyText == null) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return Text(
|
||||||
|
widget.emptyText ?? context.lang.contactLabelsSubtitleEmpty,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: widget.fontSize,
|
||||||
|
color: Theme.of(context).disabledColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Wrap(
|
||||||
|
spacing: 4,
|
||||||
|
runSpacing: 2,
|
||||||
|
children: _labels.map((label) {
|
||||||
|
final bgColor = Color(label.backgroundColor);
|
||||||
|
final textColor = Color(label.textColor);
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
padding: widget.padding,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: bgColor,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
label.name,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: widget.fontSize,
|
||||||
|
color: textColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget? buildContactLabelsSubtitle({
|
||||||
|
required int contactId,
|
||||||
|
required List<Label> labels,
|
||||||
|
Widget? additionalSubtitle,
|
||||||
|
}) {
|
||||||
|
if (additionalSubtitle != null) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
additionalSubtitle,
|
||||||
|
if (labels.isNotEmpty)
|
||||||
|
ContactLabels(contactId: contactId),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (labels.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ContactLabels(contactId: contactId);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ContactLabelsSubtitleBuilder extends StatelessWidget {
|
||||||
|
const ContactLabelsSubtitleBuilder({
|
||||||
|
required this.contactId,
|
||||||
|
required this.builder,
|
||||||
|
this.additionalSubtitle,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int contactId;
|
||||||
|
final Widget? additionalSubtitle;
|
||||||
|
final Widget Function(BuildContext context, Widget? subtitleWidget) builder;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return StreamBuilder<List<Label>>(
|
||||||
|
stream: twonlyDB.labelsDao.watchContactLabels(contactId),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final labels = snapshot.data ?? [];
|
||||||
|
final subtitle = buildContactLabelsSubtitle(
|
||||||
|
contactId: contactId,
|
||||||
|
labels: labels,
|
||||||
|
additionalSubtitle: additionalSubtitle,
|
||||||
|
);
|
||||||
|
return builder(context, subtitle);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,6 @@ import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/constants/routes.keys.dart';
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/notification_badge.comp.dart';
|
import 'package:twonly/src/visual/components/notification_badge.comp.dart';
|
||||||
import 'package:twonly/src/visual/themes/light.dart';
|
|
||||||
|
|
||||||
class ContactRequestBadgeComp extends StatelessWidget {
|
class ContactRequestBadgeComp extends StatelessWidget {
|
||||||
const ContactRequestBadgeComp({super.key});
|
const ContactRequestBadgeComp({super.key});
|
||||||
|
|
@ -26,8 +25,8 @@ class ContactRequestBadgeComp extends StatelessWidget {
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
decoration: const BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: primaryColor,
|
color: context.color.primary,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
114
lib/src/visual/components/custom_color_picker_dialog.comp.dart
Normal file
114
lib/src/visual/components/custom_color_picker_dialog.comp.dart
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
||||||
|
|
||||||
|
class CustomColorPickerDialog extends StatefulWidget {
|
||||||
|
const CustomColorPickerDialog({
|
||||||
|
required this.initialColor,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Color initialColor;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CustomColorPickerDialog> createState() => _CustomColorPickerDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CustomColorPickerDialogState extends State<CustomColorPickerDialog> {
|
||||||
|
late HSVColor _hsvColor;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_hsvColor = HSVColor.fromColor(widget.initialColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final currentColor = _hsvColor.toColor();
|
||||||
|
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(context.lang.customColor),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Color preview box
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: currentColor,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.15),
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: const Offset(0, 4),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
// Hue Slider
|
||||||
|
Text(context.lang.hue, style: const TextStyle(fontWeight: FontWeight.w600)),
|
||||||
|
Slider(
|
||||||
|
value: _hsvColor.hue,
|
||||||
|
max: 360,
|
||||||
|
activeColor: HSVColor.fromAHSV(1, _hsvColor.hue, 1, 1).toColor(),
|
||||||
|
onChanged: (val) {
|
||||||
|
setState(() {
|
||||||
|
_hsvColor = _hsvColor.withHue(val);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Saturation Slider
|
||||||
|
Text(context.lang.saturation, style: const TextStyle(fontWeight: FontWeight.w600)),
|
||||||
|
Slider(
|
||||||
|
value: _hsvColor.saturation,
|
||||||
|
onChanged: (val) {
|
||||||
|
setState(() {
|
||||||
|
_hsvColor = _hsvColor.withSaturation(val);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Brightness / Value Slider
|
||||||
|
Text(context.lang.brightness, style: const TextStyle(fontWeight: FontWeight.w600)),
|
||||||
|
Slider(
|
||||||
|
value: _hsvColor.value,
|
||||||
|
onChanged: (val) {
|
||||||
|
setState(() {
|
||||||
|
_hsvColor = _hsvColor.withValue(val);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: MyButton(
|
||||||
|
variant: MyButtonVariant.text,
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(context.lang.cancel),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: MyButton(
|
||||||
|
variant: MyButtonVariant.primaryMiddle,
|
||||||
|
onPressed: () => Navigator.of(context).pop(currentColor.toARGB32()),
|
||||||
|
child: Text(context.lang.ok),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
328
lib/src/visual/components/label_editor_bottom_sheet.comp.dart
Normal file
328
lib/src/visual/components/label_editor_bottom_sheet.comp.dart
Normal file
|
|
@ -0,0 +1,328 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/visual/components/custom_color_picker_dialog.comp.dart';
|
||||||
|
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
||||||
|
|
||||||
|
class LabelEditorBottomSheet extends StatefulWidget {
|
||||||
|
const LabelEditorBottomSheet({
|
||||||
|
this.label,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Label? label;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<LabelEditorBottomSheet> createState() => _LabelEditorBottomSheetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LabelEditorBottomSheetState extends State<LabelEditorBottomSheet> {
|
||||||
|
late TextEditingController _nameController;
|
||||||
|
late int _selectedBgColor;
|
||||||
|
late int _selectedTextColor;
|
||||||
|
|
||||||
|
static const List<int> defaultBgColors = [
|
||||||
|
0xFFE57373, // Red
|
||||||
|
0xFFF06292, // Pink
|
||||||
|
0xFFBA68C8, // Purple
|
||||||
|
0xFF7986CB, // Indigo
|
||||||
|
0xFF64B5F6, // Blue
|
||||||
|
0xFF4DD0E1, // Cyan
|
||||||
|
0xFF4DB6AC, // Teal
|
||||||
|
0xFF81C784, // Green
|
||||||
|
0xFFFFB74D, // Amber
|
||||||
|
0xFFFF8A65, // Deep Orange
|
||||||
|
0xFF90A4AE, // Blue Grey
|
||||||
|
0xFF424242, // Dark Grey
|
||||||
|
];
|
||||||
|
|
||||||
|
static const List<int> defaultTextColors = [
|
||||||
|
0xFFFFFFFF, // White
|
||||||
|
0xFF121212, // Dark/Black
|
||||||
|
0xFF1B263B, // Deep Navy
|
||||||
|
0xFF8B0000, // Dark Red
|
||||||
|
0xFF004D40, // Dark Teal
|
||||||
|
0xFF4A148C, // Dark Purple
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_nameController = TextEditingController(text: widget.label?.name ?? '');
|
||||||
|
_selectedBgColor = widget.label?.backgroundColor ?? defaultBgColors[4];
|
||||||
|
_selectedTextColor = widget.label?.textColor ?? defaultTextColors[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_nameController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _pickCustomColor({required bool isBgColor}) async {
|
||||||
|
final initial = isBgColor ? Color(_selectedBgColor) : Color(_selectedTextColor);
|
||||||
|
final pickedColorInt = await showDialog<int>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => CustomColorPickerDialog(initialColor: initial),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pickedColorInt != null && mounted) {
|
||||||
|
setState(() {
|
||||||
|
if (isBgColor) {
|
||||||
|
_selectedBgColor = pickedColorInt;
|
||||||
|
} else {
|
||||||
|
_selectedTextColor = pickedColorInt;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final isEditing = widget.label != null;
|
||||||
|
final bottomInset = MediaQuery.of(context).viewInsets.bottom;
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(bottom: bottomInset),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
// Drag indicator handle
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
width: 40,
|
||||||
|
height: 4,
|
||||||
|
margin: const EdgeInsets.only(bottom: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
borderRadius: BorderRadius.circular(2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
isEditing ? context.lang.editLabel : context.lang.createLabel,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
// Interactive Inline Label Badge
|
||||||
|
Center(
|
||||||
|
child: IntrinsicWidth(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(minWidth: 100, maxWidth: 200),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Color(_selectedBgColor),
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 2),
|
||||||
|
child: TextField(
|
||||||
|
controller: _nameController,
|
||||||
|
autofocus: true,
|
||||||
|
maxLength: 8,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
textCapitalization: TextCapitalization.words,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(_selectedTextColor),
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: context.lang.labelNameHint,
|
||||||
|
hintStyle: TextStyle(
|
||||||
|
color: Color(_selectedTextColor).withValues(alpha: 0.6),
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
border: InputBorder.none,
|
||||||
|
isDense: true,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 4, vertical: 6),
|
||||||
|
counterText: '',
|
||||||
|
),
|
||||||
|
onChanged: (_) => setState(() {}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
// Background Color Section
|
||||||
|
Text(
|
||||||
|
context.lang.labelBackgroundColor,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Wrap(
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
spacing: 10,
|
||||||
|
runSpacing: 10,
|
||||||
|
children: [
|
||||||
|
...defaultBgColors.map((colorValue) {
|
||||||
|
final selected = _selectedBgColor == colorValue;
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_selectedBgColor = colorValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Color(colorValue),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: selected
|
||||||
|
? Icon(
|
||||||
|
Icons.check,
|
||||||
|
size: 18,
|
||||||
|
color: Color(colorValue).computeLuminance() > 0.5
|
||||||
|
? Colors.black
|
||||||
|
: Colors.white,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
// Custom Color Picker Button
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () => _pickCustomColor(isBgColor: true),
|
||||||
|
child: Container(
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
gradient: SweepGradient(
|
||||||
|
colors: [
|
||||||
|
Colors.red,
|
||||||
|
Colors.yellow,
|
||||||
|
Colors.green,
|
||||||
|
Colors.cyan,
|
||||||
|
Colors.blue,
|
||||||
|
Colors.purple,
|
||||||
|
Colors.red,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.colorize,
|
||||||
|
size: 18,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
// Text Color Section
|
||||||
|
Text(
|
||||||
|
context.lang.labelTextColor,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Wrap(
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
spacing: 10,
|
||||||
|
runSpacing: 10,
|
||||||
|
children: [
|
||||||
|
...defaultTextColors.map((colorValue) {
|
||||||
|
final selected = _selectedTextColor == colorValue;
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_selectedTextColor = colorValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Color(colorValue),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: selected
|
||||||
|
? Icon(
|
||||||
|
Icons.check,
|
||||||
|
size: 18,
|
||||||
|
color: Color(colorValue).computeLuminance() > 0.5
|
||||||
|
? Colors.black
|
||||||
|
: Colors.white,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
// Custom Color Picker Button for Text
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () => _pickCustomColor(isBgColor: false),
|
||||||
|
child: Container(
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
gradient: SweepGradient(
|
||||||
|
colors: [
|
||||||
|
Colors.red,
|
||||||
|
Colors.yellow,
|
||||||
|
Colors.green,
|
||||||
|
Colors.cyan,
|
||||||
|
Colors.blue,
|
||||||
|
Colors.purple,
|
||||||
|
Colors.red,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.colorize,
|
||||||
|
size: 18,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: MyButton(
|
||||||
|
variant: MyButtonVariant.text,
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(context.lang.cancel),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: MyButton(
|
||||||
|
variant: MyButtonVariant.primaryMiddle,
|
||||||
|
onPressed: () {
|
||||||
|
final name = _nameController.text.trim();
|
||||||
|
if (name.isNotEmpty) {
|
||||||
|
Navigator.of(context).pop({
|
||||||
|
'name': name,
|
||||||
|
'backgroundColor': _selectedBgColor,
|
||||||
|
'textColor': _selectedTextColor,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(context.lang.ok),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/visual/themes/light.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
class SelectableThumbnailComp extends StatelessWidget {
|
class SelectableThumbnailComp extends StatelessWidget {
|
||||||
const SelectableThumbnailComp({
|
const SelectableThumbnailComp({
|
||||||
|
|
@ -19,7 +19,7 @@ class SelectableThumbnailComp extends StatelessWidget {
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected ? primaryColor : Colors.transparent,
|
color: isSelected ? context.color.primary : Colors.transparent,
|
||||||
boxShadow: const [
|
boxShadow: const [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black12,
|
color: Colors.black12,
|
||||||
|
|
@ -49,7 +49,7 @@ class SelectableThumbnailComp extends StatelessWidget {
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(2),
|
padding: const EdgeInsets.all(2),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected ? primaryColor : Colors.black38,
|
color: isSelected ? context.color.primary : Colors.black38,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: Theme.of(context).brightness == Brightness.dark
|
color: Theme.of(context).brightness == Brightness.dark
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/profile_qr_code.comp.dart';
|
import 'package:twonly/src/visual/components/profile_qr_code.comp.dart';
|
||||||
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
||||||
import 'package:twonly/src/visual/elements/svg_icon.element.dart';
|
import 'package:twonly/src/visual/elements/svg_icon.element.dart';
|
||||||
import 'package:twonly/src/visual/themes/light.dart';
|
|
||||||
|
|
||||||
const colorVerificationBadgeYellow = Color.fromARGB(255, 0, 182, 238);
|
const colorVerificationBadgeYellow = Color.fromARGB(255, 0, 182, 238);
|
||||||
|
|
||||||
|
|
@ -90,7 +89,7 @@ class VerificationBadgeInfo extends StatelessWidget {
|
||||||
context,
|
context,
|
||||||
icon: const SvgIcon(assetPath: SvgIcons.verifiedGreen, size: 40),
|
icon: const SvgIcon(assetPath: SvgIcons.verifiedGreen, size: 40),
|
||||||
description: context.lang.verificationBadgeGreenDesc,
|
description: context.lang.verificationBadgeGreenDesc,
|
||||||
boldTextColor: primaryColor,
|
boldTextColor: context.color.primary,
|
||||||
onTap: () => context.push(
|
onTap: () => context.push(
|
||||||
Routes.cameraQRScanner,
|
Routes.cameraQRScanner,
|
||||||
extra: {
|
extra: {
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ class VerificationSuccessAnimationState
|
||||||
'<svg viewBox="0 0 640 640"><path d="$_path2" fill="white"/></svg>';
|
'<svg viewBox="0 0 640 640"><path d="$_path2" fill="white"/></svg>';
|
||||||
|
|
||||||
static const _grey = Color(0xFF8E9AAF);
|
static const _grey = Color(0xFF8E9AAF);
|
||||||
static const Color _green = primaryColor;
|
static const Color _green = defaultPrimaryColor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ class BetterText extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// Regular expression to find URLs and domains
|
// Regular expression to find URLs and domains
|
||||||
final urlRegExp = RegExp(
|
final urlRegExp = RegExp(
|
||||||
r'(?:(?:https?://|www\.)[^\s]+|(?:[a-zA-Z0-9-]+\.[a-zA-Z]{2,}))',
|
r'''(?:(?:https?://|www\.)(?:[^\s()<>]+|\([^\s()<>]+\))+(?:\([^\s()<>]+\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))|(?:(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})''',
|
||||||
caseSensitive: false,
|
caseSensitive: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/elements/reactive_tap_feedback.element.dart';
|
import 'package:twonly/src/visual/elements/reactive_tap_feedback.element.dart';
|
||||||
import 'package:twonly/src/visual/themes/light.dart';
|
|
||||||
|
|
||||||
enum MyButtonVariant {
|
enum MyButtonVariant {
|
||||||
primary,
|
primary,
|
||||||
|
|
@ -49,7 +48,7 @@ class _MyButtonState extends State<MyButton> {
|
||||||
switch (widget.variant) {
|
switch (widget.variant) {
|
||||||
case MyButtonVariant.primary:
|
case MyButtonVariant.primary:
|
||||||
buttonStyle = FilledButton.styleFrom(
|
buttonStyle = FilledButton.styleFrom(
|
||||||
backgroundColor: primaryColor,
|
backgroundColor: context.color.primary,
|
||||||
foregroundColor: Colors.black87,
|
foregroundColor: Colors.black87,
|
||||||
disabledBackgroundColor: disabledBgColor,
|
disabledBackgroundColor: disabledBgColor,
|
||||||
disabledForegroundColor: disabledFgColor,
|
disabledForegroundColor: disabledFgColor,
|
||||||
|
|
@ -96,7 +95,7 @@ class _MyButtonState extends State<MyButton> {
|
||||||
);
|
);
|
||||||
case MyButtonVariant.primaryMiddle:
|
case MyButtonVariant.primaryMiddle:
|
||||||
buttonStyle = FilledButton.styleFrom(
|
buttonStyle = FilledButton.styleFrom(
|
||||||
backgroundColor: primaryColor,
|
backgroundColor: context.color.primary,
|
||||||
foregroundColor: Colors.black87,
|
foregroundColor: Colors.black87,
|
||||||
disabledBackgroundColor: disabledBgColor,
|
disabledBackgroundColor: disabledBgColor,
|
||||||
disabledForegroundColor: disabledFgColor,
|
disabledForegroundColor: disabledFgColor,
|
||||||
|
|
@ -115,7 +114,7 @@ class _MyButtonState extends State<MyButton> {
|
||||||
);
|
);
|
||||||
case MyButtonVariant.primaryDense:
|
case MyButtonVariant.primaryDense:
|
||||||
buttonStyle = FilledButton.styleFrom(
|
buttonStyle = FilledButton.styleFrom(
|
||||||
backgroundColor: primaryColor,
|
backgroundColor: context.color.primary,
|
||||||
foregroundColor: Colors.black87,
|
foregroundColor: Colors.black87,
|
||||||
disabledBackgroundColor: disabledBgColor,
|
disabledBackgroundColor: disabledBgColor,
|
||||||
disabledForegroundColor: disabledFgColor,
|
disabledForegroundColor: disabledFgColor,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/elements/reactive_tap_feedback.element.dart';
|
import 'package:twonly/src/visual/elements/reactive_tap_feedback.element.dart';
|
||||||
import 'package:twonly/src/visual/themes/light.dart';
|
|
||||||
|
|
||||||
enum MyIconButtonVariant {
|
enum MyIconButtonVariant {
|
||||||
primary,
|
primary,
|
||||||
|
|
@ -43,7 +42,7 @@ class _MyIconButtonState extends State<MyIconButton> {
|
||||||
late final Color fgColor;
|
late final Color fgColor;
|
||||||
|
|
||||||
if (widget.variant == MyIconButtonVariant.primary) {
|
if (widget.variant == MyIconButtonVariant.primary) {
|
||||||
bgColor = primaryColor;
|
bgColor = context.color.primary;
|
||||||
fgColor = Colors.black87;
|
fgColor = Colors.black87;
|
||||||
} else {
|
} else {
|
||||||
bgColor = isDark ? Colors.grey[800]! : Colors.grey[200]!;
|
bgColor = isDark ? Colors.grey[800]! : Colors.grey[200]!;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/src/visual/themes/light.dart';
|
||||||
|
|
||||||
final ThemeData darkTheme = () {
|
ThemeData getDarkTheme([Color primary = defaultPrimaryColor]) {
|
||||||
final base = ThemeData.dark().copyWith(
|
final base = ThemeData.dark().copyWith(
|
||||||
colorScheme: ColorScheme.fromSeed(
|
colorScheme: ColorScheme.fromSeed(
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
seedColor: const Color(0xFF57CC99),
|
seedColor: primary,
|
||||||
|
primary: primary,
|
||||||
surface: const Color.fromARGB(255, 20, 18, 23),
|
surface: const Color.fromARGB(255, 20, 18, 23),
|
||||||
surfaceContainer: const Color.fromARGB(255, 45, 41, 54),
|
surfaceContainer: const Color.fromARGB(255, 45, 41, 54),
|
||||||
surfaceContainerLow: const Color.fromARGB(255, 38, 34, 45),
|
surfaceContainerLow: const Color.fromARGB(255, 38, 34, 45),
|
||||||
|
|
@ -21,4 +23,6 @@ final ThemeData darkTheme = () {
|
||||||
fontFamilyFallback: Platform.isAndroid ? const ['NotoColorEmoji'] : null,
|
fontFamilyFallback: Platform.isAndroid ? const ['NotoColorEmoji'] : null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}();
|
}
|
||||||
|
|
||||||
|
final ThemeData darkTheme = getDarkTheme();
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,13 @@ import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
const primaryColor = Color(0xFF57CC99);
|
const defaultPrimaryColor = Color(0xFF57CC99);
|
||||||
|
|
||||||
final ThemeData lightTheme = () {
|
ThemeData getLightTheme([Color primary = defaultPrimaryColor]) {
|
||||||
final base = ThemeData(
|
final base = ThemeData(
|
||||||
colorScheme: ColorScheme.fromSeed(
|
colorScheme: ColorScheme.fromSeed(
|
||||||
seedColor: primaryColor,
|
seedColor: primary,
|
||||||
|
primary: primary,
|
||||||
),
|
),
|
||||||
inputDecorationTheme: const InputDecorationTheme(
|
inputDecorationTheme: const InputDecorationTheme(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
|
|
@ -19,10 +20,12 @@ final ThemeData lightTheme = () {
|
||||||
fontFamilyFallback: Platform.isAndroid ? const ['NotoColorEmoji'] : null,
|
fontFamilyFallback: Platform.isAndroid ? const ['NotoColorEmoji'] : null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}();
|
}
|
||||||
|
|
||||||
|
final ThemeData lightTheme = getLightTheme();
|
||||||
|
|
||||||
final ButtonStyle primaryColorButtonStyle = FilledButton.styleFrom(
|
final ButtonStyle primaryColorButtonStyle = FilledButton.styleFrom(
|
||||||
backgroundColor: primaryColor,
|
backgroundColor: defaultPrimaryColor,
|
||||||
foregroundColor: Colors.black87,
|
foregroundColor: Colors.black87,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,8 @@ class CameraScannedOverlay extends StatelessWidget {
|
||||||
width: 150,
|
width: 150,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
if (mainController.scannedUrl != null)
|
if (mainController.scannedUrl != null &&
|
||||||
|
mainController.scannedUrl!.isNotEmpty)
|
||||||
_buildScannedUrlTile(context, mainController.scannedUrl!),
|
_buildScannedUrlTile(context, mainController.scannedUrl!),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
super.initState();
|
super.initState();
|
||||||
initVolumeControl();
|
initVolumeControl();
|
||||||
initAsync();
|
initAsync();
|
||||||
|
_checkAndInitCamera();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -176,12 +177,26 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
if (oldWidget.isVisible != widget.isVisible) {
|
if (oldWidget.isVisible != widget.isVisible) {
|
||||||
if (widget.isVisible) {
|
if (widget.isVisible) {
|
||||||
initVolumeControl();
|
initVolumeControl();
|
||||||
|
_checkAndInitCamera();
|
||||||
} else {
|
} else {
|
||||||
_deInitVolumeControl();
|
_deInitVolumeControl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _checkAndInitCamera() {
|
||||||
|
if (widget.isVisible &&
|
||||||
|
mc.cameraController == null &&
|
||||||
|
!mc.initCameraStarted) {
|
||||||
|
unawaited(
|
||||||
|
mc.selectCamera(
|
||||||
|
mc.selectedCameraDetails.cameraId,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_videoRecordingTimer?.cancel();
|
_videoRecordingTimer?.cancel();
|
||||||
|
|
@ -686,6 +701,13 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (mc.selectedCameraDetails.cameraId >= AppEnvironment.cameras.length ||
|
if (mc.selectedCameraDetails.cameraId >= AppEnvironment.cameras.length ||
|
||||||
mc.cameraController == null) {
|
mc.cameraController == null) {
|
||||||
|
if (widget.isVisible &&
|
||||||
|
!mc.initCameraStarted &&
|
||||||
|
!mc.isSharePreviewIsShown) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_checkAndInitCamera();
|
||||||
|
});
|
||||||
|
}
|
||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
return StreamBuilder(
|
return StreamBuilder(
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ class MainCameraController {
|
||||||
}
|
}
|
||||||
|
|
||||||
void onImageSend() {
|
void onImageSend() {
|
||||||
scannedUrl = '';
|
scannedUrl = null;
|
||||||
setState?.call();
|
setState?.call();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,6 +155,7 @@ class MainCameraController {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.error('Error querying available cameras: $e');
|
Log.error('Error querying available cameras: $e');
|
||||||
initCameraStarted = false;
|
initCameraStarted = false;
|
||||||
|
setState?.call();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,6 +165,7 @@ class MainCameraController {
|
||||||
'Trying to select a non existing camera $cameraId >= ${AppEnvironment.cameras.length}',
|
'Trying to select a non existing camera $cameraId >= ${AppEnvironment.cameras.length}',
|
||||||
);
|
);
|
||||||
initCameraStarted = false;
|
initCameraStarted = false;
|
||||||
|
setState?.call();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,7 +194,7 @@ class MainCameraController {
|
||||||
final hasMic = await micPermissionFuture;
|
final hasMic = await micPermissionFuture;
|
||||||
if (sessionId != _cameraSessionId) return;
|
if (sessionId != _cameraSessionId) return;
|
||||||
|
|
||||||
final controller = CameraController(
|
var controller = CameraController(
|
||||||
AppEnvironment.cameras[cameraId],
|
AppEnvironment.cameras[cameraId],
|
||||||
ResolutionPreset.high,
|
ResolutionPreset.high,
|
||||||
enableAudio: hasMic,
|
enableAudio: hasMic,
|
||||||
|
|
@ -204,7 +206,23 @@ class MainCameraController {
|
||||||
var assignedToGlobal = false;
|
var assignedToGlobal = false;
|
||||||
try {
|
try {
|
||||||
_initializeFuture = controller.initialize();
|
_initializeFuture = controller.initialize();
|
||||||
|
try {
|
||||||
await _initializeFuture;
|
await _initializeFuture;
|
||||||
|
} on CameraException catch (e) {
|
||||||
|
// If specific image format is unsupported on this hardware, fallback to default format
|
||||||
|
Log.warn(
|
||||||
|
'Initial camera format initialization failed ($e), trying fallback format...',
|
||||||
|
);
|
||||||
|
await controller.dispose();
|
||||||
|
controller = CameraController(
|
||||||
|
AppEnvironment.cameras[cameraId],
|
||||||
|
ResolutionPreset.high,
|
||||||
|
enableAudio: hasMic,
|
||||||
|
);
|
||||||
|
_initializeFuture = controller.initialize();
|
||||||
|
await _initializeFuture;
|
||||||
|
}
|
||||||
|
|
||||||
if (sessionId != _cameraSessionId) {
|
if (sessionId != _cameraSessionId) {
|
||||||
unawaited(controller.dispose());
|
unawaited(controller.dispose());
|
||||||
return;
|
return;
|
||||||
|
|
@ -256,6 +274,7 @@ class MainCameraController {
|
||||||
unawaited(controller.dispose());
|
unawaited(controller.dispose());
|
||||||
}
|
}
|
||||||
initCameraStarted = false;
|
initCameraStarted = false;
|
||||||
|
setState?.call();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/key_verification.service.dart';
|
import 'package:twonly/src/services/key_verification.service.dart';
|
||||||
|
|
@ -8,6 +10,7 @@ import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview.dart';
|
import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/camera_preview_components/main_camera_controller.dart';
|
import 'package:twonly/src/visual/views/camera/camera_preview_components/main_camera_controller.dart';
|
||||||
|
import 'package:twonly/src/visual/views/camera/share_image_editor_components/action_button.dart';
|
||||||
|
|
||||||
class QrCodeScannerView extends StatefulWidget {
|
class QrCodeScannerView extends StatefulWidget {
|
||||||
const QrCodeScannerView({
|
const QrCodeScannerView({
|
||||||
|
|
@ -41,6 +44,7 @@ class QrCodeScannerViewState extends State<QrCodeScannerView> {
|
||||||
}
|
}
|
||||||
Permission.camera.isGranted.then((hasPermission) {
|
Permission.camera.isGranted.then((hasPermission) {
|
||||||
if (hasPermission && mounted) {
|
if (hasPermission && mounted) {
|
||||||
|
AppState.hasCameraPermissions = true;
|
||||||
unawaited(_mainCameraController.selectCamera(0, true));
|
unawaited(_mainCameraController.selectCamera(0, true));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -100,6 +104,17 @@ class QrCodeScannerViewState extends State<QrCodeScannerView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// Always-visible back button so the user can exit regardless of
|
||||||
|
// camera state (e.g. during init or after init failure).
|
||||||
|
Positioned(
|
||||||
|
left: 5,
|
||||||
|
top: MediaQuery.paddingOf(context).top + 10,
|
||||||
|
child: ActionButton(
|
||||||
|
FontAwesomeIcons.xmark,
|
||||||
|
tooltipText: context.lang.close,
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview.dart';
|
import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/camera_preview_components/main_camera_controller.dart';
|
import 'package:twonly/src/visual/views/camera/camera_preview_components/main_camera_controller.dart';
|
||||||
|
import 'package:twonly/src/visual/views/camera/share_image_editor_components/action_button.dart';
|
||||||
|
|
||||||
class CameraSendToView extends StatefulWidget {
|
class CameraSendToView extends StatefulWidget {
|
||||||
const CameraSendToView(this.sendToGroup, {super.key});
|
const CameraSendToView(this.sendToGroup, {super.key});
|
||||||
|
|
@ -24,6 +28,7 @@ class CameraSendToViewState extends State<CameraSendToView> {
|
||||||
};
|
};
|
||||||
Permission.camera.isGranted.then((hasPermission) {
|
Permission.camera.isGranted.then((hasPermission) {
|
||||||
if (hasPermission && mounted) {
|
if (hasPermission && mounted) {
|
||||||
|
AppState.hasCameraPermissions = true;
|
||||||
unawaited(_mainCameraController.selectCamera(0, true));
|
unawaited(_mainCameraController.selectCamera(0, true));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -58,6 +63,17 @@ class CameraSendToViewState extends State<CameraSendToView> {
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// Always-visible back button so the user can exit regardless of
|
||||||
|
// camera state (e.g. during init or after init failure).
|
||||||
|
Positioned(
|
||||||
|
left: 5,
|
||||||
|
top: MediaQuery.paddingOf(context).top + 10,
|
||||||
|
child: ActionButton(
|
||||||
|
FontAwesomeIcons.xmark,
|
||||||
|
tooltipText: context.lang.close,
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -310,10 +310,12 @@ class _ShareImageView extends State<ShareImageView> {
|
||||||
});
|
});
|
||||||
|
|
||||||
// in case mediaStoreFutureReady is ready, the image is stored in the originalPath
|
// in case mediaStoreFutureReady is ready, the image is stored in the originalPath
|
||||||
await insertMediaFileInMessagesTable(
|
unawaited(
|
||||||
|
insertMediaFileInMessagesTable(
|
||||||
widget.mediaFileService,
|
widget.mediaFileService,
|
||||||
widget.selectedGroupIds.toList(),
|
widget.selectedGroupIds.toList(),
|
||||||
additionalData: widget.additionalData,
|
additionalData: widget.additionalData,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
|
|
|
||||||
|
|
@ -359,11 +359,12 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
context.lang.dialogAskDeleteMediaFilePopTitle,
|
context.lang.dialogAskDeleteMediaFilePopTitle,
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
FilledButton(
|
MyButton(
|
||||||
child: Text(context.lang.dialogAskDeleteMediaFilePopDelete),
|
variant: MyButtonVariant.primaryMiddle,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context, true);
|
Navigator.pop(context, true);
|
||||||
},
|
},
|
||||||
|
child: Text(context.lang.dialogAskDeleteMediaFilePopDelete),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
child: Text(context.lang.cancel),
|
child: Text(context.lang.cancel),
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ class _ArchivedChatsViewState extends State<ArchivedChatsView> {
|
||||||
body: ListView(
|
body: ListView(
|
||||||
children: _groupsArchived.map((group) {
|
children: _groupsArchived.map((group) {
|
||||||
return GroupListItemComp(
|
return GroupListItemComp(
|
||||||
|
key: ValueKey(group.groupId),
|
||||||
group: group,
|
group: group,
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/connection_status.comp.dart';
|
import 'package:twonly/src/visual/components/connection_status.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/notification_badge.comp.dart';
|
import 'package:twonly/src/visual/components/notification_badge.comp.dart';
|
||||||
import 'package:twonly/src/visual/themes/light.dart';
|
|
||||||
import 'package:twonly/src/visual/views/chats/chat_list_components/empty_chat_list.comp.dart';
|
import 'package:twonly/src/visual/views/chats/chat_list_components/empty_chat_list.comp.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/chat_list_components/group_list_item.comp.dart';
|
import 'package:twonly/src/visual/views/chats/chat_list_components/group_list_item.comp.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/chat_list_components/news_btn.comp.dart';
|
import 'package:twonly/src/visual/views/chats/chat_list_components/news_btn.comp.dart';
|
||||||
|
|
@ -196,8 +195,8 @@ class _ChatListViewState extends State<ChatListView> with AutomaticKeepAliveClie
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
decoration: const BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: primaryColor,
|
color: context.color.primary,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -329,7 +328,7 @@ class _ChatListViewState extends State<ChatListView> with AutomaticKeepAliveClie
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
heroTag: 'new_chat_fab',
|
heroTag: 'new_chat_fab',
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
backgroundColor: primaryColor,
|
backgroundColor: context.color.primary,
|
||||||
foregroundColor: Colors.black87,
|
foregroundColor: Colors.black87,
|
||||||
onPressed: () => context.push(Routes.chatsStartNewChat),
|
onPressed: () => context.push(Routes.chatsStartNewChat),
|
||||||
child: const FaIcon(
|
child: const FaIcon(
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,13 @@ import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/constants/routes.keys.dart';
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/database/tables/messages.table.dart';
|
import 'package:twonly/src/database/tables/messages.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/download.api.dart';
|
import 'package:twonly/src/services/api/mediafiles/download.api.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
||||||
|
import 'package:twonly/src/visual/components/contact_labels.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
|
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
|
||||||
import 'package:twonly/src/visual/context_menu/group.context_menu.dart';
|
import 'package:twonly/src/visual/context_menu/group.context_menu.dart';
|
||||||
|
|
@ -42,6 +42,8 @@ class _UserListItem extends State<GroupListItemComp> {
|
||||||
StreamSubscription<Message?>? _lastMessageStream;
|
StreamSubscription<Message?>? _lastMessageStream;
|
||||||
StreamSubscription<Reaction?>? _lastReactionStream;
|
StreamSubscription<Reaction?>? _lastReactionStream;
|
||||||
StreamSubscription<List<MediaFile>>? _lastMediaFilesStream;
|
StreamSubscription<List<MediaFile>>? _lastMediaFilesStream;
|
||||||
|
Contact? _directContact;
|
||||||
|
StreamSubscription<List<Contact>>? _directContactStream;
|
||||||
|
|
||||||
List<Message> _previewMessages = [];
|
List<Message> _previewMessages = [];
|
||||||
final List<MediaFile> _previewMediaFiles = [];
|
final List<MediaFile> _previewMediaFiles = [];
|
||||||
|
|
@ -60,6 +62,7 @@ class _UserListItem extends State<GroupListItemComp> {
|
||||||
_lastReactionStream?.cancel();
|
_lastReactionStream?.cancel();
|
||||||
_lastMessageStream?.cancel();
|
_lastMessageStream?.cancel();
|
||||||
_lastMediaFilesStream?.cancel();
|
_lastMediaFilesStream?.cancel();
|
||||||
|
_directContactStream?.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,6 +105,19 @@ class _UserListItem extends State<GroupListItemComp> {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (widget.group.isDirectChat) {
|
||||||
|
_directContactStream = twonlyDB.groupsDao
|
||||||
|
.watchGroupContact(widget.group.groupId)
|
||||||
|
.listen((contacts) {
|
||||||
|
if (!mounted) return;
|
||||||
|
if (contacts.isNotEmpty) {
|
||||||
|
setState(() {
|
||||||
|
_directContact = contacts.first;
|
||||||
|
_receiverDeletedAccount = _directContact!.accountDeleted;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
final groupContacts = await twonlyDB.groupsDao.getGroupContact(
|
final groupContacts = await twonlyDB.groupsDao.getGroupContact(
|
||||||
widget.group.groupId,
|
widget.group.groupId,
|
||||||
);
|
);
|
||||||
|
|
@ -110,6 +126,7 @@ class _UserListItem extends State<GroupListItemComp> {
|
||||||
_receiverDeletedAccount = groupContacts.first.accountDeleted;
|
_receiverDeletedAccount = groupContacts.first.accountDeleted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _updateState(
|
void _updateState(
|
||||||
Message? newLastMessage,
|
Message? newLastMessage,
|
||||||
|
|
@ -239,8 +256,12 @@ class _UserListItem extends State<GroupListItemComp> {
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Flexible(
|
||||||
substringBy(widget.group.groupName, 30),
|
child: Text(
|
||||||
|
widget.group.groupName,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 3),
|
const SizedBox(width: 3),
|
||||||
VerificationBadgeComp(
|
VerificationBadgeComp(
|
||||||
|
|
@ -249,6 +270,18 @@ class _UserListItem extends State<GroupListItemComp> {
|
||||||
clickable: false,
|
clickable: false,
|
||||||
size: 12,
|
size: 12,
|
||||||
),
|
),
|
||||||
|
if (widget.group.isDirectChat && _directContact != null) ...[
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Flexible(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
physics: const BouncingScrollPhysics(),
|
||||||
|
child: ContactLabels(
|
||||||
|
contactId: _directContact!.userId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
subtitle: _receiverDeletedAccount
|
subtitle: _receiverDeletedAccount
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:clock/clock.dart';
|
import 'package:clock/clock.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/locator.dart';
|
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
||||||
|
|
@ -42,12 +41,12 @@ class _LastMessageTimeCompState extends State<LastMessageTimeComp> {
|
||||||
_actionSubscription = null;
|
_actionSubscription = null;
|
||||||
|
|
||||||
if (widget.message != null) {
|
if (widget.message != null) {
|
||||||
_actionSubscription = twonlyDB.messagesDao
|
if (widget.message!.senderId == null) {
|
||||||
.watchLastMessageAction(widget.message!.messageId)
|
targetTime = widget.message!.openedAt ?? widget.message!.createdAt;
|
||||||
.listen((lastAction) {
|
} else {
|
||||||
targetTime = lastAction?.actionAt ?? widget.message!.createdAt;
|
targetTime = widget.message!.createdAt;
|
||||||
|
}
|
||||||
_updateSeconds();
|
_updateSeconds();
|
||||||
});
|
|
||||||
} else if (widget.dateTime != null) {
|
} else if (widget.dateTime != null) {
|
||||||
targetTime = widget.dateTime;
|
targetTime = widget.dateTime;
|
||||||
_updateSeconds();
|
_updateSeconds();
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/constants/routes.keys.dart';
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/notification_badge.comp.dart';
|
import 'package:twonly/src/visual/components/notification_badge.comp.dart';
|
||||||
import 'package:twonly/src/visual/themes/light.dart';
|
|
||||||
|
|
||||||
class NewsIconButtonComp extends StatelessWidget {
|
class NewsIconButtonComp extends StatelessWidget {
|
||||||
const NewsIconButtonComp({super.key});
|
const NewsIconButtonComp({super.key});
|
||||||
|
|
@ -23,7 +22,7 @@ class NewsIconButtonComp extends StatelessWidget {
|
||||||
builder: (context, count, child) {
|
builder: (context, count, child) {
|
||||||
return NotificationBadgeComp(
|
return NotificationBadgeComp(
|
||||||
count: count.toString(),
|
count: count.toString(),
|
||||||
backgroundColor: primaryColor,
|
backgroundColor: context.color.primary,
|
||||||
textColor: Colors.black87,
|
textColor: Colors.black87,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () => context.push(Routes.settingsHelpNews),
|
onPressed: () => context.push(Routes.settingsHelpNews),
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import 'package:twonly/src/services/api/messages.api.dart';
|
||||||
import 'package:twonly/src/services/notifications/background.notifications.dart';
|
import 'package:twonly/src/services/notifications/background.notifications.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
||||||
|
import 'package:twonly/src/visual/components/contact_labels.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
|
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
|
||||||
import 'package:twonly/src/visual/themes/colors.dart';
|
import 'package:twonly/src/visual/themes/colors.dart';
|
||||||
|
|
@ -234,13 +235,20 @@ class _ChatMessagesViewState extends State<ChatMessagesView>
|
||||||
messages = chatItems.reversed.toList();
|
messages = chatItems.reversed.toList();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (wasSentByMe && itemScrollController.isAttached) {
|
if (wasSentByMe) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (!mounted || !itemScrollController.isAttached) return;
|
||||||
|
try {
|
||||||
unawaited(
|
unawaited(
|
||||||
itemScrollController.scrollTo(
|
itemScrollController.scrollTo(
|
||||||
index: 0,
|
index: 0,
|
||||||
duration: const Duration(milliseconds: 150),
|
duration: const Duration(milliseconds: 150),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// Ignore if the inner scroll controller is still not attached
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
final items = await MemoryItem.convertFromMessages(storedMediaFiles);
|
final items = await MemoryItem.convertFromMessages(storedMediaFiles);
|
||||||
|
|
@ -303,7 +311,11 @@ class _ChatMessagesViewState extends State<ChatMessagesView>
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ColoredBox(
|
child: ColoredBox(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: Row(
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
substringBy(group.groupName, 20),
|
substringBy(group.groupName, 20),
|
||||||
|
|
@ -317,6 +329,23 @@ class _ChatMessagesViewState extends State<ChatMessagesView>
|
||||||
FlameCounterWidget(groupId: group.groupId),
|
FlameCounterWidget(groupId: group.groupId),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
if (group.isDirectChat)
|
||||||
|
StreamBuilder<List<Contact>>(
|
||||||
|
stream: twonlyDB.groupsDao.watchGroupContact(
|
||||||
|
group.groupId,
|
||||||
|
),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final contacts = snapshot.data ?? [];
|
||||||
|
if (contacts.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return ContactLabels(
|
||||||
|
contactId: contacts.first.userId,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,12 @@ import 'package:twonly/src/model/protobuf/client/generated/messages.pbserver.dar
|
||||||
import 'package:twonly/src/services/api/messages.api.dart';
|
import 'package:twonly/src/services/api/messages.api.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
|
||||||
import 'package:twonly/src/visual/components/emoji_picker.bottom.dart';
|
import 'package:twonly/src/visual/components/emoji_picker.bottom.dart';
|
||||||
import 'package:twonly/src/visual/context_menu/context_menu.helper.dart';
|
import 'package:twonly/src/visual/context_menu/context_menu.helper.dart';
|
||||||
|
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layer_data.dart';
|
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layer_data.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/message_info.view.dart';
|
import 'package:twonly/src/visual/views/chats/message_info.view.dart';
|
||||||
|
import 'package:twonly/src/visual/views/chats/chat_messages_components/chat_list_entry.dart';
|
||||||
import 'package:twonly/src/visual/views/memories/synchronized_viewer.view.dart';
|
import 'package:twonly/src/visual/views/memories/synchronized_viewer.view.dart';
|
||||||
|
|
||||||
class MessageContextMenu extends StatelessWidget {
|
class MessageContextMenu extends StatelessWidget {
|
||||||
|
|
@ -83,8 +84,9 @@ class MessageContextMenu extends StatelessWidget {
|
||||||
return SynchronizedImageViewerScreen(
|
return SynchronizedImageViewerScreen(
|
||||||
galleryItems: galleryItems,
|
galleryItems: galleryItems,
|
||||||
initialIndex: 0,
|
initialIndex: 0,
|
||||||
activeMediaIdNotifier:
|
activeMediaIdNotifier: ValueNotifier(
|
||||||
ValueNotifier(mediaFileService!.mediaFile.mediaId),
|
mediaFileService!.mediaFile.mediaId,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
||||||
|
|
@ -172,17 +174,13 @@ class MessageContextMenu extends StatelessWidget {
|
||||||
ContextMenuItem(
|
ContextMenuItem(
|
||||||
title: context.lang.delete,
|
title: context.lang.delete,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final delete = await showAlertDialog(
|
final action = await showDeleteMessageOptions(
|
||||||
navigator.context,
|
navigator.context,
|
||||||
navigator.context.lang.deleteTitle,
|
message,
|
||||||
null,
|
group,
|
||||||
customOk:
|
galleryItems,
|
||||||
(message.senderId == null && !message.isDeletedFromSender)
|
|
||||||
? navigator.context.lang.deleteOkBtnForAll
|
|
||||||
: navigator.context.lang.deleteOkBtnForMe,
|
|
||||||
);
|
);
|
||||||
if (delete) {
|
if (action == 'delete_for_all') {
|
||||||
if (message.senderId == null && !message.isDeletedFromSender) {
|
|
||||||
await twonlyDB.messagesDao.handleMessageDeletion(
|
await twonlyDB.messagesDao.handleMessageDeletion(
|
||||||
null,
|
null,
|
||||||
message.messageId,
|
message.messageId,
|
||||||
|
|
@ -197,12 +195,11 @@ class MessageContextMenu extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else if (action == 'delete_for_me') {
|
||||||
await twonlyDB.messagesDao.deleteMessagesById(
|
await twonlyDB.messagesDao.deleteMessagesById(
|
||||||
message.messageId,
|
message.messageId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
icon: FontAwesomeIcons.trash,
|
icon: FontAwesomeIcons.trash,
|
||||||
),
|
),
|
||||||
|
|
@ -230,22 +227,138 @@ class MessageContextMenu extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String?> showDeleteMessageOptions(
|
||||||
|
BuildContext context,
|
||||||
|
Message message,
|
||||||
|
Group group,
|
||||||
|
List<MemoryItem> galleryItems,
|
||||||
|
) async {
|
||||||
|
return showModalBottomSheet<String>(
|
||||||
|
context: context,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
builder: (context) {
|
||||||
|
final isForAll = message.senderId == null && !message.isDeletedFromSender;
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
top: 20,
|
||||||
|
bottom: 40,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: context.color.surfaceContainer,
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(24),
|
||||||
|
topRight: Radius.circular(24),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
ChatListEntry(
|
||||||
|
group: group,
|
||||||
|
message: message,
|
||||||
|
galleryItems: galleryItems,
|
||||||
|
),
|
||||||
|
Positioned.fill(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
// Prevent clicks
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
color: Colors.transparent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
if (isForAll) ...[
|
||||||
|
Center(
|
||||||
|
child: MyButton(
|
||||||
|
variant: MyButtonVariant.errorMiddle,
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context, 'delete_for_all');
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
context.lang.deleteOkBtnForAll,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
],
|
||||||
|
Center(
|
||||||
|
child: MyButton(
|
||||||
|
variant: isForAll
|
||||||
|
? MyButtonVariant.secondaryDense
|
||||||
|
: MyButtonVariant.errorMiddle,
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context, 'delete_for_me');
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
isForAll
|
||||||
|
? context.lang.deleteOnlyForMe
|
||||||
|
: context.lang.deleteOkBtnForMe,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Center(
|
||||||
|
child: MyButton(
|
||||||
|
variant: MyButtonVariant.text,
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context, 'cancel');
|
||||||
|
},
|
||||||
|
child: Text(context.lang.cancel, textAlign: TextAlign.center),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> editTextMessage(BuildContext context, Message message) async {
|
Future<void> editTextMessage(BuildContext context, Message message) async {
|
||||||
var newText = message.content;
|
var newText = message.content;
|
||||||
final controller = TextEditingController(text: message.content);
|
final controller = TextEditingController(text: message.content);
|
||||||
await showDialog(
|
await showModalBottomSheet<void>(
|
||||||
context: context,
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return StatefulBuilder(
|
||||||
content: StatefulBuilder(
|
|
||||||
builder: (context, setState) {
|
builder: (context, setState) {
|
||||||
return SingleChildScrollView(
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
top: 20,
|
||||||
|
bottom: 40,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: context.color.surfaceContainer,
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(24),
|
||||||
|
topRight: Radius.circular(24),
|
||||||
|
),
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
TextField(
|
||||||
padding: const EdgeInsets.only(top: 8),
|
|
||||||
child: TextField(
|
|
||||||
controller: controller,
|
controller: controller,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
textCapitalization: TextCapitalization.sentences,
|
textCapitalization: TextCapitalization.sentences,
|
||||||
|
|
@ -259,20 +372,25 @@ Future<void> editTextMessage(BuildContext context, Message message) async {
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 24),
|
||||||
],
|
Row(
|
||||||
),
|
children: [
|
||||||
);
|
Expanded(
|
||||||
},
|
child: MyButton(
|
||||||
),
|
variant: MyButtonVariant.secondaryMiddle,
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: Text(context.lang.cancel),
|
child: Text(
|
||||||
|
context.lang.cancel,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
TextButton(
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: MyButton(
|
||||||
|
variant: MyButtonVariant.primaryMiddle,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (newText != null &&
|
if (newText != null &&
|
||||||
newText != message.content &&
|
newText != message.content &&
|
||||||
|
|
@ -288,8 +406,11 @@ Future<void> editTextMessage(BuildContext context, Message message) async {
|
||||||
await sendCipherTextToGroup(
|
await sendCipherTextToGroup(
|
||||||
message.groupId,
|
message.groupId,
|
||||||
pb.EncryptedContent(
|
pb.EncryptedContent(
|
||||||
messageUpdate: pb.EncryptedContent_MessageUpdate(
|
messageUpdate:
|
||||||
type: pb.EncryptedContent_MessageUpdate_Type.EDIT_TEXT,
|
pb.EncryptedContent_MessageUpdate(
|
||||||
|
type: pb
|
||||||
|
.EncryptedContent_MessageUpdate_Type
|
||||||
|
.EDIT_TEXT,
|
||||||
senderMessageId: message.messageId,
|
senderMessageId: message.messageId,
|
||||||
text: newText,
|
text: newText,
|
||||||
timestamp: Int64(
|
timestamp: Int64(
|
||||||
|
|
@ -302,9 +423,19 @@ Future<void> editTextMessage(BuildContext context, Message message) async {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: Text(context.lang.ok),
|
child: Text(
|
||||||
|
context.lang.ok,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,8 @@ class _MessageInputState extends State<MessageInput> {
|
||||||
Timer? _nextTypingIndicator;
|
Timer? _nextTypingIndicator;
|
||||||
DateTime? _lastTextChangeTime;
|
DateTime? _lastTextChangeTime;
|
||||||
int? _contactId;
|
int? _contactId;
|
||||||
|
Timer? _recordingTimer;
|
||||||
|
DateTime? _recordingStartTime;
|
||||||
|
|
||||||
Future<void> _sendMessage() async {
|
Future<void> _sendMessage() async {
|
||||||
if (_textFieldController.text == '') return;
|
if (_textFieldController.text == '') return;
|
||||||
|
|
@ -104,6 +106,7 @@ class _MessageInputState extends State<MessageInput> {
|
||||||
_textFieldController.removeListener(_handleTextChange);
|
_textFieldController.removeListener(_handleTextChange);
|
||||||
widget.textFieldFocus.removeListener(_handleTextFocusChange);
|
widget.textFieldFocus.removeListener(_handleTextFocusChange);
|
||||||
widget.textFieldFocus.dispose();
|
widget.textFieldFocus.dispose();
|
||||||
|
_recordingTimer?.cancel();
|
||||||
recorderController.dispose();
|
recorderController.dispose();
|
||||||
_nextTypingIndicator?.cancel();
|
_nextTypingIndicator?.cancel();
|
||||||
|
|
||||||
|
|
@ -124,12 +127,6 @@ class _MessageInputState extends State<MessageInput> {
|
||||||
|
|
||||||
void _initializeControllers() {
|
void _initializeControllers() {
|
||||||
recorderController = RecorderController();
|
recorderController = RecorderController();
|
||||||
recorderController.onCurrentDuration.listen((duration) {
|
|
||||||
if (!mounted) return;
|
|
||||||
setState(() {
|
|
||||||
_currentDuration = duration.inMilliseconds;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleTextChange() {
|
void _handleTextChange() {
|
||||||
|
|
@ -161,6 +158,21 @@ class _MessageInputState extends State<MessageInput> {
|
||||||
_recordingState = RecordingState.recording;
|
_recordingState = RecordingState.recording;
|
||||||
_currentDuration = 0;
|
_currentDuration = 0;
|
||||||
});
|
});
|
||||||
|
_recordingStartTime = clock.now();
|
||||||
|
_recordingTimer?.cancel();
|
||||||
|
_recordingTimer = Timer.periodic(const Duration(milliseconds: 100), (
|
||||||
|
timer,
|
||||||
|
) {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
if (_recordingStartTime != null) {
|
||||||
|
_currentDuration = clock
|
||||||
|
.now()
|
||||||
|
.difference(_recordingStartTime!)
|
||||||
|
.inMilliseconds;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
await HapticFeedback.heavyImpact();
|
await HapticFeedback.heavyImpact();
|
||||||
final audioTmpPath = '${AppEnvironment.cacheDir}/recording.m4a';
|
final audioTmpPath = '${AppEnvironment.cacheDir}/recording.m4a';
|
||||||
unawaited(
|
unawaited(
|
||||||
|
|
@ -171,6 +183,8 @@ class _MessageInputState extends State<MessageInput> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _stopAudioRecording() async {
|
Future<void> _stopAudioRecording() async {
|
||||||
|
_recordingTimer?.cancel();
|
||||||
|
_recordingTimer = null;
|
||||||
await HapticFeedback.heavyImpact();
|
await HapticFeedback.heavyImpact();
|
||||||
setState(() {
|
setState(() {
|
||||||
_audioRecordingLock = false;
|
_audioRecordingLock = false;
|
||||||
|
|
@ -200,6 +214,8 @@ class _MessageInputState extends State<MessageInput> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _cancelAudioRecording() async {
|
Future<void> _cancelAudioRecording() async {
|
||||||
|
_recordingTimer?.cancel();
|
||||||
|
_recordingTimer = null;
|
||||||
setState(() {
|
setState(() {
|
||||||
_audioRecordingLock = false;
|
_audioRecordingLock = false;
|
||||||
_cancelSlideOffset = 0;
|
_cancelSlideOffset = 0;
|
||||||
|
|
@ -330,8 +346,7 @@ class _MessageInputState extends State<MessageInput> {
|
||||||
TextField(
|
TextField(
|
||||||
controller: _textFieldController,
|
controller: _textFieldController,
|
||||||
focusNode: widget.textFieldFocus,
|
focusNode: widget.textFieldFocus,
|
||||||
textCapitalization:
|
textCapitalization: TextCapitalization.sentences,
|
||||||
TextCapitalization.sentences,
|
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
showCursor:
|
showCursor:
|
||||||
_recordingState != RecordingState.recording,
|
_recordingState != RecordingState.recording,
|
||||||
|
|
@ -385,13 +400,10 @@ class _MessageInputState extends State<MessageInput> {
|
||||||
),
|
),
|
||||||
if (!_audioRecordingLock) ...[
|
if (!_audioRecordingLock) ...[
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width:
|
width: (100 - _cancelSlideOffset) % 101,
|
||||||
(100 - _cancelSlideOffset) % 101,
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
context
|
context.lang.voiceMessageSlideToCancel,
|
||||||
.lang
|
|
||||||
.voiceMessageSlideToCancel,
|
|
||||||
),
|
),
|
||||||
] else ...[
|
] else ...[
|
||||||
Expanded(
|
Expanded(
|
||||||
|
|
@ -476,8 +488,7 @@ class _MessageInputState extends State<MessageInput> {
|
||||||
child: Stack(
|
child: Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
children: [
|
children: [
|
||||||
if (_recordingState ==
|
if (_recordingState == RecordingState.recording &&
|
||||||
RecordingState.recording &&
|
|
||||||
!_audioRecordingLock)
|
!_audioRecordingLock)
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
top: -120,
|
top: -120,
|
||||||
|
|
@ -513,8 +524,7 @@ class _MessageInputState extends State<MessageInput> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (_recordingState ==
|
if (_recordingState == RecordingState.recording &&
|
||||||
RecordingState.recording &&
|
|
||||||
!_audioRecordingLock)
|
!_audioRecordingLock)
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
top: -20,
|
top: -20,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
|
|
@ -206,7 +207,15 @@ class _MessageInfoViewState extends State<MessageInfoView> {
|
||||||
'${context.lang.received}: ${friendlyDateTime(context, widget.message.ackByServer!)}',
|
'${context.lang.received}: ${friendlyDateTime(context, widget.message.ackByServer!)}',
|
||||||
),
|
),
|
||||||
if (userService.currentUser.isDeveloper)
|
if (userService.currentUser.isDeveloper)
|
||||||
Text('ID: ${widget.message.messageId}'),
|
GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
|
await Clipboard.setData(
|
||||||
|
ClipboardData(text: widget.message.messageId),
|
||||||
|
);
|
||||||
|
await HapticFeedback.heavyImpact();
|
||||||
|
},
|
||||||
|
child: Text('ID: ${widget.message.messageId}'),
|
||||||
|
),
|
||||||
if (messageHistory.isNotEmpty) ...[
|
if (messageHistory.isNotEmpty) ...[
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
||||||
|
import 'package:twonly/src/visual/components/contact_labels.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
|
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
|
||||||
import 'package:twonly/src/visual/context_menu/group.context_menu.dart';
|
import 'package:twonly/src/visual/context_menu/group.context_menu.dart';
|
||||||
|
|
@ -216,32 +217,39 @@ class _StartNewChatView extends State<StartNewChatView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i < filteredContacts.length) {
|
if (i < filteredContacts.length) {
|
||||||
|
final contact = filteredContacts[i];
|
||||||
return UserContextMenu(
|
return UserContextMenu(
|
||||||
key: ValueKey(filteredContacts[i].userId),
|
key: ValueKey(contact.userId),
|
||||||
contact: filteredContacts[i],
|
contact: contact,
|
||||||
child: ListTile(
|
child: ContactLabelsSubtitleBuilder(
|
||||||
|
contactId: contact.userId,
|
||||||
|
builder: (context, subtitleWidget) {
|
||||||
|
return ListTile(
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(getContactDisplayName(filteredContacts[i])),
|
Text(getContactDisplayName(contact)),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
right: 8,
|
right: 8,
|
||||||
left: 1,
|
left: 1,
|
||||||
),
|
),
|
||||||
child: VerificationBadgeComp(
|
child: VerificationBadgeComp(
|
||||||
contact: filteredContacts[i],
|
contact: contact,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
FlameCounterWidget(
|
FlameCounterWidget(
|
||||||
contactId: filteredContacts[i].userId,
|
contactId: contact.userId,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
subtitle: subtitleWidget,
|
||||||
leading: AvatarIcon(
|
leading: AvatarIcon(
|
||||||
contactId: filteredContacts[i].userId,
|
contactId: contact.userId,
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
),
|
),
|
||||||
onTap: () => _onTapUser(filteredContacts[i]),
|
onTap: () => _onTapUser(contact),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,13 @@ import 'package:go_router/go_router.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/constants/routes.keys.dart';
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
|
import 'package:twonly/src/database/tables/contacts.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
||||||
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
||||||
|
import 'package:twonly/src/visual/components/contact_labels.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/select_chat_deletion_time.comp.dart';
|
import 'package:twonly/src/visual/components/select_chat_deletion_time.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/snackbar.dart';
|
import 'package:twonly/src/visual/components/snackbar.dart';
|
||||||
|
|
@ -20,6 +23,7 @@ import 'package:twonly/src/visual/views/contact/contact_components/mutual_groups
|
||||||
import 'package:twonly/src/visual/views/contact/contact_components/restore_flame.comp.dart';
|
import 'package:twonly/src/visual/views/contact/contact_components/restore_flame.comp.dart';
|
||||||
import 'package:twonly/src/visual/views/contact/contact_components/user_discovery_contact_settings.comp.dart';
|
import 'package:twonly/src/visual/views/contact/contact_components/user_discovery_contact_settings.comp.dart';
|
||||||
import 'package:twonly/src/visual/views/contact/contact_components/verification_expansion_tile.comp.dart';
|
import 'package:twonly/src/visual/views/contact/contact_components/verification_expansion_tile.comp.dart';
|
||||||
|
import 'package:twonly/src/visual/views/contact/select_contact_labels.view.dart';
|
||||||
import 'package:twonly/src/visual/views/groups/group.view.dart';
|
import 'package:twonly/src/visual/views/groups/group.view.dart';
|
||||||
|
|
||||||
class ContactView extends StatefulWidget {
|
class ContactView extends StatefulWidget {
|
||||||
|
|
@ -225,6 +229,21 @@ class _ContactViewState extends State<ContactView> {
|
||||||
userService.currentUser.userId,
|
userService.currentUser.userId,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
ContactLabelsSubtitleBuilder(
|
||||||
|
contactId: contact.userId,
|
||||||
|
builder: (context, subtitleWidget) {
|
||||||
|
return BetterListTile(
|
||||||
|
icon: FontAwesomeIcons.tag,
|
||||||
|
text: context.lang.contactLabelsTitle,
|
||||||
|
subtitle: subtitleWidget,
|
||||||
|
onTap: () {
|
||||||
|
context.navPush(
|
||||||
|
SelectContactLabelsView(contactId: contact.userId),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
RestoreFlameComp(
|
RestoreFlameComp(
|
||||||
contactId: widget.userId,
|
contactId: widget.userId,
|
||||||
|
|
@ -256,6 +275,48 @@ class _ContactViewState extends State<ContactView> {
|
||||||
text: context.lang.contactRemove,
|
text: context.lang.contactRemove,
|
||||||
onTap: () => handleUserRemoveRequest(contact),
|
onTap: () => handleUserRemoveRequest(contact),
|
||||||
),
|
),
|
||||||
|
if (userService.currentUser.isDeveloper) ...[
|
||||||
|
if (contact.signalVersion != SignalVersion.v2)
|
||||||
|
BetterListTile(
|
||||||
|
icon: FontAwesomeIcons.arrowsRotate,
|
||||||
|
text: 'Update Connection to V2 (PQXDH)',
|
||||||
|
onTap: () async {
|
||||||
|
final userData = await apiService.getUserById(contact.userId);
|
||||||
|
if (userData != null) {
|
||||||
|
await processSignalUserData(userData);
|
||||||
|
final updatedContact = await twonlyDB.contactsDao
|
||||||
|
.getContactById(contact.userId);
|
||||||
|
final isV2 =
|
||||||
|
updatedContact?.signalVersion == SignalVersion.v2;
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
showSnackbar(
|
||||||
|
context,
|
||||||
|
isV2
|
||||||
|
? 'Connection updated to V2 successfully'
|
||||||
|
: 'Failed to update connection to V2',
|
||||||
|
level: isV2
|
||||||
|
? SnackbarLevel.success
|
||||||
|
: SnackbarLevel.error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'Encryption: ${switch (contact.signalVersion) {
|
||||||
|
SignalVersion.v1 => 'Signal Protocol (v1)',
|
||||||
|
SignalVersion.v2 => 'PQXDH (v2)',
|
||||||
|
}}',
|
||||||
|
style: const TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,9 @@ class _RestoreFlameCompState extends State<RestoreFlameComp> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (!userService.currentUser.showRestoreFlame) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
if (_group == null || !isItPossibleToRestoreFlames(_group!)) {
|
if (_group == null || !isItPossibleToRestoreFlames(_group!)) {
|
||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +118,7 @@ class _RestoreFlameCompState extends State<RestoreFlameComp> {
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
onTap: _restoreFlames,
|
onTap: _restoreFlames,
|
||||||
title: Text(
|
title: Text(
|
||||||
'Restore your ${_group!.maxFlameCounter} lost flames',
|
context.lang.restoreLostFlames(_group!.maxFlameCounter),
|
||||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||||
),
|
),
|
||||||
trailing: const SizedBox(
|
trailing: const SizedBox(
|
||||||
|
|
@ -134,7 +137,7 @@ class _RestoreFlameCompState extends State<RestoreFlameComp> {
|
||||||
emoji: '🔥',
|
emoji: '🔥',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
text: 'Restore your ${_group!.maxFlameCounter} lost flames',
|
text: context.lang.restoreLostFlames(_group!.maxFlameCounter),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
313
lib/src/visual/views/contact/select_contact_labels.view.dart
Normal file
313
lib/src/visual/views/contact/select_contact_labels.view.dart
Normal file
|
|
@ -0,0 +1,313 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:twonly/locator.dart';
|
||||||
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
||||||
|
import 'package:twonly/src/visual/components/contact_labels.comp.dart';
|
||||||
|
import 'package:twonly/src/visual/components/label_editor_bottom_sheet.comp.dart';
|
||||||
|
import 'package:twonly/src/visual/components/snackbar.dart';
|
||||||
|
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
||||||
|
|
||||||
|
class SelectContactLabelsView extends StatefulWidget {
|
||||||
|
const SelectContactLabelsView({
|
||||||
|
required this.contactId,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int contactId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SelectContactLabelsView> createState() => _SelectContactLabelsViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SelectContactLabelsViewState extends State<SelectContactLabelsView> {
|
||||||
|
Contact? _contact;
|
||||||
|
List<Label> _allLabels = [];
|
||||||
|
Set<int> _selectedLabelIds = {};
|
||||||
|
|
||||||
|
late StreamSubscription<Contact?> _contactSub;
|
||||||
|
late StreamSubscription<List<Label>> _allLabelsSub;
|
||||||
|
late StreamSubscription<List<Label>> _contactLabelsSub;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_contactSub = twonlyDB.contactsDao.watchContact(widget.contactId).listen((contact) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_contact = contact;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_allLabelsSub = twonlyDB.labelsDao.watchAllLabels().listen((labels) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_allLabels = labels;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_contactLabelsSub = twonlyDB.labelsDao.watchContactLabels(widget.contactId).listen((labels) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_selectedLabelIds = labels.map((l) => l.id).toSet();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_contactSub.cancel();
|
||||||
|
_allLabelsSub.cancel();
|
||||||
|
_contactLabelsSub.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _toggleLabel(int labelId) async {
|
||||||
|
final newSet = Set<int>.from(_selectedLabelIds);
|
||||||
|
if (newSet.contains(labelId)) {
|
||||||
|
newSet.remove(labelId);
|
||||||
|
} else {
|
||||||
|
if (newSet.length >= 3) {
|
||||||
|
showSnackbar(
|
||||||
|
context,
|
||||||
|
context.lang.contactLabelsMaxLimit,
|
||||||
|
level: SnackbarLevel.warning,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
newSet.add(labelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_selectedLabelIds = newSet;
|
||||||
|
});
|
||||||
|
|
||||||
|
await twonlyDB.labelsDao.setContactLabels(widget.contactId, _selectedLabelIds.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _createLabel() async {
|
||||||
|
final result = await showModalBottomSheet<Map<String, dynamic>>(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
builder: (context) => const LabelEditorBottomSheet(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != null && mounted) {
|
||||||
|
final name = result['name'] as String;
|
||||||
|
final bgColor = result['backgroundColor'] as int;
|
||||||
|
final textColor = result['textColor'] as int;
|
||||||
|
|
||||||
|
await twonlyDB.labelsDao.createLabel(name, textColor, bgColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _editLabel(Label label) async {
|
||||||
|
final result = await showModalBottomSheet<Map<String, dynamic>>(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
builder: (context) => LabelEditorBottomSheet(label: label),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != null && mounted) {
|
||||||
|
final name = result['name'] as String;
|
||||||
|
final bgColor = result['backgroundColor'] as int;
|
||||||
|
final textColor = result['textColor'] as int;
|
||||||
|
|
||||||
|
await twonlyDB.labelsDao.updateLabel(label.id, name, textColor, bgColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _deleteLabel(Label label) async {
|
||||||
|
final confirm = await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: Text(context.lang.deleteLabel),
|
||||||
|
content: Text(context.lang.deleteLabelConfirmation),
|
||||||
|
actions: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: MyButton(
|
||||||
|
variant: MyButtonVariant.text,
|
||||||
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
|
child: Text(context.lang.cancel),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: MyButton(
|
||||||
|
variant: MyButtonVariant.errorMiddle,
|
||||||
|
onPressed: () => Navigator.of(context).pop(true),
|
||||||
|
child: Text(context.lang.deleteLabel),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if ((confirm ?? false) && mounted) {
|
||||||
|
await twonlyDB.labelsDao.deleteLabel(label.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildContactHeader() {
|
||||||
|
if (_contact == null) return const SizedBox.shrink();
|
||||||
|
final contact = _contact!;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.all(12),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).cardColor,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
AvatarIcon(contactId: contact.userId, fontSize: 24),
|
||||||
|
const SizedBox(width: 14),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
getContactDisplayName(contact, maxLength: 25),
|
||||||
|
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
if (getContactDisplayName(contact) != contact.username)
|
||||||
|
Text(
|
||||||
|
'@${contact.username}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Theme.of(context).disabledColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
ContactLabels(contactId: contact.userId),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(context.lang.contactLabelsTitle),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
tooltip: context.lang.createLabel,
|
||||||
|
onPressed: _createLabel,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
_buildContactHeader(),
|
||||||
|
Expanded(
|
||||||
|
child: _allLabels.isEmpty
|
||||||
|
? Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
context.lang.contactLabelsSubtitleEmpty,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Theme.of(context).disabledColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
MyButton(
|
||||||
|
variant: MyButtonVariant.primaryMiddle,
|
||||||
|
onPressed: _createLabel,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.add, size: 20),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Text(context.lang.createLabel),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: ListView.builder(
|
||||||
|
itemCount: _allLabels.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final label = _allLabels[index];
|
||||||
|
final isSelected = _selectedLabelIds.contains(label.id);
|
||||||
|
|
||||||
|
return CheckboxListTile(
|
||||||
|
value: isSelected,
|
||||||
|
onChanged: (_) => _toggleLabel(label.id),
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Color(label.backgroundColor),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
label.name,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(label.textColor),
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
secondary: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Tooltip(
|
||||||
|
message: context.lang.editLabel,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
onTap: () => _editLabel(label),
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.all(6),
|
||||||
|
child: FaIcon(FontAwesomeIcons.penToSquare, size: 16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 2),
|
||||||
|
Tooltip(
|
||||||
|
message: context.lang.deleteLabel,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
onTap: () => _deleteLabel(label),
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.all(6),
|
||||||
|
child: FaIcon(FontAwesomeIcons.trashCan, size: 16, color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ import 'package:twonly/src/services/group.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
||||||
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
||||||
|
import 'package:twonly/src/visual/components/contact_labels.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/select_chat_deletion_time.comp.dart';
|
import 'package:twonly/src/visual/components/select_chat_deletion_time.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/snackbar.dart';
|
import 'package:twonly/src/visual/components/snackbar.dart';
|
||||||
|
|
@ -248,7 +249,10 @@ class _GroupViewState extends State<GroupView> {
|
||||||
group: _group!,
|
group: _group!,
|
||||||
contact: member.$1,
|
contact: member.$1,
|
||||||
member: member.$2,
|
member: member.$2,
|
||||||
child: BetterListTile(
|
child: ContactLabelsSubtitleBuilder(
|
||||||
|
contactId: member.$1.userId,
|
||||||
|
builder: (context, subtitleWidget) {
|
||||||
|
return BetterListTile(
|
||||||
padding: const EdgeInsets.only(left: 13),
|
padding: const EdgeInsets.only(left: 13),
|
||||||
leading: AvatarIcon(
|
leading: AvatarIcon(
|
||||||
contactId: member.$1.userId,
|
contactId: member.$1.userId,
|
||||||
|
|
@ -266,6 +270,7 @@ class _GroupViewState extends State<GroupView> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
subtitle: subtitleWidget,
|
||||||
trailing: (member.$2.memberState == MemberState.admin)
|
trailing: (member.$2.memberState == MemberState.admin)
|
||||||
? Text(context.lang.admin)
|
? Text(context.lang.admin)
|
||||||
: null,
|
: null,
|
||||||
|
|
@ -277,6 +282,8 @@ class _GroupViewState extends State<GroupView> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,12 @@ import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/group.service.dart';
|
import 'package:twonly/src/services/group.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
||||||
|
import 'package:twonly/src/visual/components/contact_labels.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
||||||
|
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
|
||||||
import 'package:twonly/src/visual/context_menu/user.context_menu.dart';
|
import 'package:twonly/src/visual/context_menu/user.context_menu.dart';
|
||||||
import 'package:twonly/src/visual/decorations/input_text.decoration.dart';
|
import 'package:twonly/src/visual/decorations/input_text.decoration.dart';
|
||||||
|
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
||||||
|
|
||||||
class GroupCreateSelectGroupNameView extends StatefulWidget {
|
class GroupCreateSelectGroupNameView extends StatefulWidget {
|
||||||
const GroupCreateSelectGroupNameView({
|
const GroupCreateSelectGroupNameView({
|
||||||
|
|
@ -59,20 +62,28 @@ class _GroupCreateSelectGroupNameViewState
|
||||||
title: Text(context.lang.selectGroupName),
|
title: Text(context.lang.selectGroupName),
|
||||||
),
|
),
|
||||||
floatingActionButtonAnimator: FloatingActionButtonAnimator.noAnimation,
|
floatingActionButtonAnimator: FloatingActionButtonAnimator.noAnimation,
|
||||||
floatingActionButton: FilledButton.icon(
|
floatingActionButton: MyButton(
|
||||||
|
variant: MyButtonVariant.primaryMiddle,
|
||||||
onPressed: (textFieldGroupName.text.isEmpty || _isLoading)
|
onPressed: (textFieldGroupName.text.isEmpty || _isLoading)
|
||||||
? null
|
? null
|
||||||
: _createNewGroup,
|
: _createNewGroup,
|
||||||
label: Text(context.lang.createGroup),
|
child: Row(
|
||||||
icon: _isLoading
|
mainAxisSize: MainAxisSize.min,
|
||||||
? const SizedBox(
|
children: [
|
||||||
|
if (_isLoading)
|
||||||
|
const SizedBox(
|
||||||
width: 15,
|
width: 15,
|
||||||
height: 15,
|
height: 15,
|
||||||
child: CircularProgressIndicator.adaptive(
|
child: CircularProgressIndicator.adaptive(
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: const FaIcon(FontAwesomeIcons.penToSquare),
|
else
|
||||||
|
const FaIcon(FontAwesomeIcons.penToSquare, size: 16),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(context.lang.createGroup),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
@ -111,20 +122,35 @@ class _GroupCreateSelectGroupNameViewState
|
||||||
return UserContextMenu(
|
return UserContextMenu(
|
||||||
key: ValueKey(user.userId),
|
key: ValueKey(user.userId),
|
||||||
contact: user,
|
contact: user,
|
||||||
child: ListTile(
|
child: ContactLabelsSubtitleBuilder(
|
||||||
|
contactId: user.userId,
|
||||||
|
builder: (context, subtitleWidget) {
|
||||||
|
return ListTile(
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(getContactDisplayName(user)),
|
Text(getContactDisplayName(user)),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
right: 8,
|
||||||
|
left: 1,
|
||||||
|
),
|
||||||
|
child: VerificationBadgeComp(
|
||||||
|
contact: user,
|
||||||
|
),
|
||||||
|
),
|
||||||
FlameCounterWidget(
|
FlameCounterWidget(
|
||||||
contactId: user.userId,
|
contactId: user.userId,
|
||||||
prefix: true,
|
prefix: true,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
subtitle: subtitleWidget,
|
||||||
leading: AvatarIcon(
|
leading: AvatarIcon(
|
||||||
contactId: user.userId,
|
contactId: user.userId,
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,14 @@ import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
||||||
|
import 'package:twonly/src/visual/components/contact_labels.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
||||||
import 'package:twonly/src/visual/components/snackbar.dart';
|
import 'package:twonly/src/visual/components/snackbar.dart';
|
||||||
|
import 'package:twonly/src/visual/components/verification_badge.comp.dart';
|
||||||
import 'package:twonly/src/visual/context_menu/user.context_menu.dart';
|
import 'package:twonly/src/visual/context_menu/user.context_menu.dart';
|
||||||
import 'package:twonly/src/visual/decorations/input_text.decoration.dart';
|
import 'package:twonly/src/visual/decorations/input_text.decoration.dart';
|
||||||
import 'package:twonly/src/visual/elements/contact_chip.element.dart';
|
import 'package:twonly/src/visual/elements/contact_chip.element.dart';
|
||||||
|
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
||||||
import 'package:twonly/src/visual/views/groups/group_create_select_group_name.view.dart';
|
import 'package:twonly/src/visual/views/groups/group_create_select_group_name.view.dart';
|
||||||
|
|
||||||
class GroupCreateSelectMembersView extends StatefulWidget {
|
class GroupCreateSelectMembersView extends StatefulWidget {
|
||||||
|
|
@ -131,14 +134,21 @@ class _StartNewChatView extends State<GroupCreateSelectMembersView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
floatingActionButtonAnimator: FloatingActionButtonAnimator.noAnimation,
|
floatingActionButtonAnimator: FloatingActionButtonAnimator.noAnimation,
|
||||||
floatingActionButton: FilledButton.icon(
|
floatingActionButton: MyButton(
|
||||||
|
variant: MyButtonVariant.primaryMiddle,
|
||||||
onPressed: selectedUsers.isEmpty ? null : submitChanges,
|
onPressed: selectedUsers.isEmpty ? null : submitChanges,
|
||||||
label: Text(
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const FaIcon(FontAwesomeIcons.penToSquare, size: 16),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
widget.groupId == null
|
widget.groupId == null
|
||||||
? context.lang.next
|
? context.lang.next
|
||||||
: context.lang.updateGroup,
|
: context.lang.updateGroup,
|
||||||
),
|
),
|
||||||
icon: const FaIcon(FontAwesomeIcons.penToSquare),
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
@ -209,19 +219,32 @@ class _StartNewChatView extends State<GroupCreateSelectMembersView> {
|
||||||
return UserContextMenu(
|
return UserContextMenu(
|
||||||
key: ValueKey(user.userId),
|
key: ValueKey(user.userId),
|
||||||
contact: user,
|
contact: user,
|
||||||
child: ListTile(
|
child: ContactLabelsSubtitleBuilder(
|
||||||
|
contactId: user.userId,
|
||||||
|
additionalSubtitle: alreadyInGroup.contains(user.userId)
|
||||||
|
? Text(context.lang.alreadyInGroup)
|
||||||
|
: null,
|
||||||
|
builder: (context, subtitleWidget) {
|
||||||
|
return ListTile(
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(getContactDisplayName(user)),
|
Text(getContactDisplayName(user)),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
right: 8,
|
||||||
|
left: 1,
|
||||||
|
),
|
||||||
|
child: VerificationBadgeComp(
|
||||||
|
contact: user,
|
||||||
|
),
|
||||||
|
),
|
||||||
FlameCounterWidget(
|
FlameCounterWidget(
|
||||||
contactId: user.userId,
|
contactId: user.userId,
|
||||||
prefix: true,
|
prefix: true,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
subtitle: (alreadyInGroup.contains(user.userId))
|
subtitle: subtitleWidget,
|
||||||
? Text(context.lang.alreadyInGroup)
|
|
||||||
: null,
|
|
||||||
leading: AvatarIcon(
|
leading: AvatarIcon(
|
||||||
contactId: user.userId,
|
contactId: user.userId,
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
|
|
@ -247,6 +270,8 @@ class _StartNewChatView extends State<GroupCreateSelectMembersView> {
|
||||||
onTap: () {
|
onTap: () {
|
||||||
toggleSelectedUser(user.userId);
|
toggleSelectedUser(user.userId);
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ class HomeViewState extends State<HomeView> with WidgetsBindingObserver {
|
||||||
StreamSubscription<int>? _homeViewPageIndexSub;
|
StreamSubscription<int>? _homeViewPageIndexSub;
|
||||||
StreamSubscription<NotificationResponse>? _selectNotificationSub;
|
StreamSubscription<NotificationResponse>? _selectNotificationSub;
|
||||||
|
|
||||||
|
static Uri? pendingSharedLink;
|
||||||
static final streamHomeViewPageIndex = StreamController<int>.broadcast();
|
static final streamHomeViewPageIndex = StreamController<int>.broadcast();
|
||||||
static final streamSharedLink = StreamController<Uri>.broadcast();
|
static final streamSharedLink = StreamController<Uri>.broadcast();
|
||||||
|
|
||||||
|
|
@ -56,7 +57,10 @@ class HomeViewState extends State<HomeView> with WidgetsBindingObserver {
|
||||||
super.initState();
|
super.initState();
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
var initialPage = widget.initialPage;
|
var initialPage = widget.initialPage;
|
||||||
if (initialPage == 1 && !userService.currentUser.startWithCameraOpen) {
|
if (HomeViewState.pendingSharedLink != null) {
|
||||||
|
initialPage = 1;
|
||||||
|
} else if (initialPage == 1 &&
|
||||||
|
!userService.currentUser.startWithCameraOpen) {
|
||||||
initialPage = 0;
|
initialPage = 0;
|
||||||
}
|
}
|
||||||
_activePageIdx = initialPage;
|
_activePageIdx = initialPage;
|
||||||
|
|
@ -100,6 +104,22 @@ class HomeViewState extends State<HomeView> with WidgetsBindingObserver {
|
||||||
streamHomeViewPageIndex.add(0);
|
streamHomeViewPageIndex.add(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_sharedLinkSub = streamSharedLink.stream.listen((uri) {
|
||||||
|
HomeViewState.pendingSharedLink = null;
|
||||||
|
_mainCameraController.setSharedLinkForPreview(uri);
|
||||||
|
Permission.camera.isGranted.then((hasPermission) {
|
||||||
|
if (hasPermission && mounted) {
|
||||||
|
unawaited(_mainCameraController.selectCamera(0, true));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (HomeViewState.pendingSharedLink != null) {
|
||||||
|
final link = HomeViewState.pendingSharedLink!;
|
||||||
|
HomeViewState.pendingSharedLink = null;
|
||||||
|
_mainCameraController.setSharedLinkForPreview(link);
|
||||||
|
}
|
||||||
|
|
||||||
if (initialPage == 1) {
|
if (initialPage == 1) {
|
||||||
Permission.camera.isGranted.then((hasPermission) {
|
Permission.camera.isGranted.then((hasPermission) {
|
||||||
if (hasPermission && mounted) {
|
if (hasPermission && mounted) {
|
||||||
|
|
@ -110,14 +130,11 @@ class HomeViewState extends State<HomeView> with WidgetsBindingObserver {
|
||||||
|
|
||||||
unawaited(_initAsync());
|
unawaited(_initAsync());
|
||||||
|
|
||||||
_sharedLinkSub = streamSharedLink.stream.listen(
|
|
||||||
_mainCameraController.setSharedLinkForPreview,
|
|
||||||
);
|
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (widget.initialPage == 1 &&
|
if (_mainCameraController.sharedLinkForPreview == null &&
|
||||||
!userService.currentUser.startWithCameraOpen ||
|
((widget.initialPage == 1 &&
|
||||||
widget.initialPage == 0) {
|
!userService.currentUser.startWithCameraOpen) ||
|
||||||
|
widget.initialPage == 0)) {
|
||||||
streamHomeViewPageIndex.add(0);
|
streamHomeViewPageIndex.add(0);
|
||||||
}
|
}
|
||||||
Future.delayed(const Duration(seconds: 1), () {
|
Future.delayed(const Duration(seconds: 1), () {
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,8 @@ class _MemoriesThumbnailCompState extends State<MemoriesThumbnailComp> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_imageProvider != null) {
|
if (_imageProvider != null) {
|
||||||
|
_imageStream?.removeListener(_listener);
|
||||||
|
_imageStream = null;
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final config = createLocalImageConfiguration(context);
|
final config = createLocalImageConfiguration(context);
|
||||||
|
|
@ -189,11 +191,22 @@ class _MemoriesThumbnailCompState extends State<MemoriesThumbnailComp> {
|
||||||
gaplessPlayback: true,
|
gaplessPlayback: true,
|
||||||
errorBuilder: (context, error, stackTrace) {
|
errorBuilder: (context, error, stackTrace) {
|
||||||
if (error.toString().contains('Invalid image data')) {
|
if (error.toString().contains('Invalid image data')) {
|
||||||
if (_selectedImageFile != null) {
|
final fileToDelete = _selectedImageFile;
|
||||||
_selectedImageFile?.deleteSync();
|
_selectedImageFile = null;
|
||||||
|
if (fileToDelete != null) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (!mounted) return;
|
||||||
|
try {
|
||||||
|
if (fileToDelete.existsSync()) {
|
||||||
|
fileToDelete.deleteSync();
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
if (_retries < 3) {
|
||||||
_retries++;
|
_retries++;
|
||||||
_resolveImage();
|
_resolveImage();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Log.warn(error);
|
Log.warn(error);
|
||||||
return ColoredBox(
|
return ColoredBox(
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,7 @@ class _RegisterViewState extends State<RegisterView> {
|
||||||
widget.callbackOnSuccess();
|
widget.callbackOnSuccess();
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
Log.error('Error creating new user', error: e, stackTrace: stack);
|
Log.error('Error creating new user', error: e, stackTrace: stack);
|
||||||
|
await deleteLocalUserData();
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isTryingToRegister = false;
|
_isTryingToRegister = false;
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ class _BackupSetupPageState extends State<BackupSetupPage> {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'twonly Backup',
|
context.lang.settingsBackup,
|
||||||
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import 'package:twonly/src/visual/components/avatar_icon.comp.dart'
|
||||||
show AvatarIcon;
|
show AvatarIcon;
|
||||||
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
||||||
import 'package:twonly/src/visual/elements/my_input.element.dart';
|
import 'package:twonly/src/visual/elements/my_input.element.dart';
|
||||||
import 'package:twonly/src/visual/themes/light.dart';
|
|
||||||
import 'package:twonly/src/visual/views/onboarding/setup/components/next_button.comp.dart';
|
import 'package:twonly/src/visual/views/onboarding/setup/components/next_button.comp.dart';
|
||||||
|
|
||||||
class ProfileSetupPage extends StatefulWidget {
|
class ProfileSetupPage extends StatefulWidget {
|
||||||
|
|
@ -67,7 +66,7 @@ class _ProfileSetupPageState extends State<ProfileSetupPage> {
|
||||||
foregroundDecoration: BoxDecoration(
|
foregroundDecoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: primaryColor,
|
color: context.color.primary,
|
||||||
width: 4,
|
width: 4,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/providers/settings.provider.dart';
|
import 'package:twonly/src/providers/settings.provider.dart';
|
||||||
import 'package:twonly/src/services/user.service.dart';
|
import 'package:twonly/src/services/user.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/visual/components/custom_color_picker_dialog.comp.dart';
|
||||||
import 'package:twonly/src/visual/elements/radio_button.element.dart';
|
import 'package:twonly/src/visual/elements/radio_button.element.dart';
|
||||||
|
|
||||||
class AppearanceView extends StatefulWidget {
|
class AppearanceView extends StatefulWidget {
|
||||||
|
|
@ -36,7 +37,7 @@ class _AppearanceViewState extends State<AppearanceView> {
|
||||||
RadioButton<ThemeMode>(
|
RadioButton<ThemeMode>(
|
||||||
value: ThemeMode.system,
|
value: ThemeMode.system,
|
||||||
groupValue: selectedValue,
|
groupValue: selectedValue,
|
||||||
label: 'System default',
|
label: context.lang.themeSystemDefault,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
selectedValue = value;
|
selectedValue = value;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
|
@ -45,7 +46,7 @@ class _AppearanceViewState extends State<AppearanceView> {
|
||||||
RadioButton<ThemeMode>(
|
RadioButton<ThemeMode>(
|
||||||
value: ThemeMode.light,
|
value: ThemeMode.light,
|
||||||
groupValue: selectedValue,
|
groupValue: selectedValue,
|
||||||
label: 'Light',
|
label: context.lang.themeLight,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
selectedValue = value;
|
selectedValue = value;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
|
@ -54,7 +55,7 @@ class _AppearanceViewState extends State<AppearanceView> {
|
||||||
RadioButton<ThemeMode>(
|
RadioButton<ThemeMode>(
|
||||||
value: ThemeMode.dark,
|
value: ThemeMode.dark,
|
||||||
groupValue: selectedValue,
|
groupValue: selectedValue,
|
||||||
label: 'Dark',
|
label: context.lang.themeDark,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
selectedValue = value;
|
selectedValue = value;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
|
@ -72,6 +73,124 @@ class _AppearanceViewState extends State<AppearanceView> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _showSelectPrimaryColor(BuildContext context) async {
|
||||||
|
const presetColors = <Color>[
|
||||||
|
Color(0xFF57CC99), // Original Twonly Green
|
||||||
|
Color(0xFF3A76F0), // Signal Blue
|
||||||
|
Color(0xFF9D4EDD), // Purple
|
||||||
|
Color(0xFFFF5964), // Coral Red
|
||||||
|
Color(0xFFFF9F1C), // Amber Orange
|
||||||
|
];
|
||||||
|
|
||||||
|
await showModalBottomSheet<void>(
|
||||||
|
context: context,
|
||||||
|
showDragHandle: true,
|
||||||
|
builder: (context) {
|
||||||
|
final activeColor =
|
||||||
|
context.watch<SettingsChangeProvider>().primaryColor;
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(24, 8, 24, 32),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
context.lang.settingsAppearancePrimaryColor,
|
||||||
|
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: presetColors.map((color) {
|
||||||
|
final isSelected =
|
||||||
|
activeColor.toARGB32() == color.toARGB32();
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
|
await context
|
||||||
|
.read<SettingsChangeProvider>()
|
||||||
|
.updatePrimaryColor(color);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: color.withValues(alpha: 0.4),
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: const Offset(0, 3),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
border: Border.all(
|
||||||
|
color:
|
||||||
|
isSelected ? Colors.white : Colors.transparent,
|
||||||
|
width: 3,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: isSelected
|
||||||
|
? const Icon(
|
||||||
|
Icons.check,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 24,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
leading: Container(
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
gradient: const SweepGradient(
|
||||||
|
colors: [
|
||||||
|
Colors.red,
|
||||||
|
Colors.yellow,
|
||||||
|
Colors.green,
|
||||||
|
Colors.cyan,
|
||||||
|
Colors.blue,
|
||||||
|
Color(0xFFFF00FF),
|
||||||
|
Colors.red,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
border: Border.all(color: Colors.white24, width: 2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(context.lang.customColor),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () async {
|
||||||
|
final pickedValue = await showDialog<int>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => CustomColorPickerDialog(
|
||||||
|
initialColor: activeColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (pickedValue != null && context.mounted) {
|
||||||
|
final pickedColor = Color(pickedValue);
|
||||||
|
await context
|
||||||
|
.read<SettingsChangeProvider>()
|
||||||
|
.updatePrimaryColor(pickedColor);
|
||||||
|
if (context.mounted) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> toggleShowNewsIcon() async {
|
Future<void> toggleShowNewsIcon() async {
|
||||||
await UserService.update((u) {
|
await UserService.update((u) {
|
||||||
u.showNewsShortcut = !u.showNewsShortcut;
|
u.showNewsShortcut = !u.showNewsShortcut;
|
||||||
|
|
@ -90,9 +209,22 @@ class _AppearanceViewState extends State<AppearanceView> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _themeModeLabel(BuildContext context, ThemeMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case ThemeMode.system:
|
||||||
|
return context.lang.themeSystemDefault;
|
||||||
|
case ThemeMode.light:
|
||||||
|
return context.lang.themeLight;
|
||||||
|
case ThemeMode.dark:
|
||||||
|
return context.lang.themeDark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final selectedTheme = context.watch<SettingsChangeProvider>().themeMode;
|
final selectedTheme = context.watch<SettingsChangeProvider>().themeMode;
|
||||||
|
final primaryColor = context.watch<SettingsChangeProvider>().primaryColor;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(context.lang.settingsAppearance),
|
title: Text(context.lang.settingsAppearance),
|
||||||
|
|
@ -105,13 +237,28 @@ class _AppearanceViewState extends State<AppearanceView> {
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(context.lang.settingsAppearanceTheme),
|
title: Text(context.lang.settingsAppearanceTheme),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
selectedTheme.name,
|
_themeModeLabel(context, selectedTheme),
|
||||||
style: const TextStyle(color: Colors.grey),
|
style: const TextStyle(color: Colors.grey),
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await _showSelectThemeMode(context);
|
await _showSelectThemeMode(context);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(context.lang.settingsAppearancePrimaryColor),
|
||||||
|
trailing: Container(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: primaryColor,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(color: Colors.white, width: 1.5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () async {
|
||||||
|
await _showSelectPrimaryColor(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(context.lang.hideNewsIcon),
|
title: Text(context.lang.hideNewsIcon),
|
||||||
onTap: toggleShowNewsIcon,
|
onTap: toggleShowNewsIcon,
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,23 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/constants/routes.keys.dart';
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/model/json/backup.model.dart';
|
import 'package:twonly/src/model/json/backup.model.dart';
|
||||||
|
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart'
|
||||||
|
as server;
|
||||||
|
import 'package:twonly/src/providers/purchases.provider.dart';
|
||||||
import 'package:twonly/src/services/backup.service.dart';
|
import 'package:twonly/src/services/backup.service.dart';
|
||||||
|
import 'package:twonly/src/services/memories/memories_cloud.service.dart';
|
||||||
|
import 'package:twonly/src/services/subscription.service.dart';
|
||||||
|
import 'package:twonly/src/services/user.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/visual/elements/better_list_title.element.dart';
|
||||||
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
||||||
|
import 'package:twonly/src/visual/views/settings/backup/memories_backup_detail.view.dart';
|
||||||
import 'package:twonly/src/visual/views/settings/backup/passwordless_recovery/components/status.passwordless_recovery.comp.dart';
|
import 'package:twonly/src/visual/views/settings/backup/passwordless_recovery/components/status.passwordless_recovery.comp.dart';
|
||||||
import 'package:twonly/src/visual/views/settings/backup/passwordless_recovery/setup.passwordless_recovery.view.dart';
|
import 'package:twonly/src/visual/views/settings/backup/passwordless_recovery/setup.passwordless_recovery.view.dart';
|
||||||
|
|
||||||
|
|
@ -21,6 +31,7 @@ class BackupView extends StatefulWidget {
|
||||||
class _BackupViewState extends State<BackupView> {
|
class _BackupViewState extends State<BackupView> {
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
CurrentBackupStatus? _backupStatus;
|
CurrentBackupStatus? _backupStatus;
|
||||||
|
server.Response_MemoriesUsage? _memoriesUsage;
|
||||||
StreamSubscription<void>? _backupUpdateSub;
|
StreamSubscription<void>? _backupUpdateSub;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -41,51 +52,29 @@ class _BackupViewState extends State<BackupView> {
|
||||||
Future<void> _loadBackupStatus() async {
|
Future<void> _loadBackupStatus() async {
|
||||||
setState(() => _isLoading = true);
|
setState(() => _isLoading = true);
|
||||||
final status = await BackupService.getData();
|
final status = await BackupService.getData();
|
||||||
|
final memoriesUsage = await apiService.getMemoriesUsage();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_backupStatus = status;
|
_backupStatus = status;
|
||||||
|
_memoriesUsage = memoriesUsage;
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
String _getBackupStatusString(LastBackupUploadState status) {
|
String _buildTileSubtitle(DateTime? date, int? size) {
|
||||||
switch (status) {
|
if (date == null) return '-';
|
||||||
case LastBackupUploadState.none:
|
final dateStr = formatRelativeDateTime(context, date);
|
||||||
return context.lang.backupPending;
|
if (size != null && size > 0) {
|
||||||
case LastBackupUploadState.pending:
|
return '$dateStr • ${formatBytes(size)}';
|
||||||
return context.lang.backupPending;
|
|
||||||
case LastBackupUploadState.failed:
|
|
||||||
return context.lang.backupFailed;
|
|
||||||
case LastBackupUploadState.success:
|
|
||||||
return context.lang.backupSuccess;
|
|
||||||
}
|
}
|
||||||
}
|
return dateStr;
|
||||||
|
|
||||||
List<TableRow> _buildTableRows(List<(String, String)> rows) {
|
|
||||||
return rows.map((pair) {
|
|
||||||
return TableRow(
|
|
||||||
children: [
|
|
||||||
TableCell(
|
|
||||||
child: Text(pair.$1),
|
|
||||||
),
|
|
||||||
TableCell(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 4,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
pair.$2,
|
|
||||||
textAlign: TextAlign.right,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final currentPlan = context.watch<PurchasesProvider>().plan;
|
||||||
|
final isFreePlan = currentPlan == SubscriptionPlan.Free;
|
||||||
|
|
||||||
return StreamBuilder<void>(
|
return StreamBuilder<void>(
|
||||||
stream: userService.onUserUpdated,
|
stream: userService.onUserUpdated,
|
||||||
builder: (context, _) {
|
builder: (context, _) {
|
||||||
|
|
@ -94,100 +83,18 @@ class _BackupViewState extends State<BackupView> {
|
||||||
title: Text(context.lang.settingsBackup),
|
title: Text(context.lang.settingsBackup),
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text(
|
|
||||||
context.lang.backupTwonlySafeDesc,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
|
|
||||||
if (userService.currentUser.passwordLessRecovery != null)
|
if (userService.currentUser.passwordLessRecovery != null)
|
||||||
const PasswordLessRecoveryStatus(),
|
const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
if (userService.currentUser.isBackupEnabled)
|
child: PasswordLessRecoveryStatus(),
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
Center(
|
|
||||||
child: Text(
|
|
||||||
context.lang.backupIdentityHeader,
|
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Table(
|
|
||||||
defaultVerticalAlignment:
|
|
||||||
TableCellVerticalAlignment.middle,
|
|
||||||
children: _buildTableRows([
|
|
||||||
(
|
|
||||||
context.lang.backupLastBackupDate,
|
|
||||||
_backupStatus?.identityLastSuccessFull != null
|
|
||||||
? formatDateTime(
|
|
||||||
context,
|
|
||||||
_backupStatus!.identityLastSuccessFull,
|
|
||||||
)
|
)
|
||||||
: '-',
|
else
|
||||||
),
|
Padding(
|
||||||
(
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
context.lang.backupLastBackupSize,
|
child: Center(
|
||||||
_backupStatus?.identitySize != null
|
|
||||||
? formatBytes(_backupStatus!.identitySize!)
|
|
||||||
: '-',
|
|
||||||
),
|
|
||||||
(
|
|
||||||
context.lang.backupLastBackupResult,
|
|
||||||
_getBackupStatusString(
|
|
||||||
_backupStatus?.identityState ??
|
|
||||||
LastBackupUploadState.none,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
Center(
|
|
||||||
child: Text(
|
|
||||||
context.lang.backupArchiveHeader,
|
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Table(
|
|
||||||
defaultVerticalAlignment:
|
|
||||||
TableCellVerticalAlignment.middle,
|
|
||||||
children: _buildTableRows([
|
|
||||||
(
|
|
||||||
context.lang.backupLastBackupDate,
|
|
||||||
_backupStatus?.archiveLastSuccessFull != null
|
|
||||||
? formatDateTime(
|
|
||||||
context,
|
|
||||||
_backupStatus!.archiveLastSuccessFull,
|
|
||||||
)
|
|
||||||
: '-',
|
|
||||||
),
|
|
||||||
(
|
|
||||||
context.lang.backupLastBackupSize,
|
|
||||||
_backupStatus?.archiveSize != null
|
|
||||||
? formatBytes(_backupStatus!.archiveSize!)
|
|
||||||
: '-',
|
|
||||||
),
|
|
||||||
(
|
|
||||||
context.lang.backupLastBackupResult,
|
|
||||||
_getBackupStatusString(
|
|
||||||
_backupStatus?.archiveState ??
|
|
||||||
LastBackupUploadState.none,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
if (userService.currentUser.passwordLessRecovery == null) ...[
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Center(
|
|
||||||
child: MyButton(
|
child: MyButton(
|
||||||
variant: MyButtonVariant.primaryMiddle,
|
variant: MyButtonVariant.primaryMiddle,
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
|
|
@ -195,8 +102,86 @@ class _BackupViewState extends State<BackupView> {
|
||||||
child: Text(context.lang.passwordlessRecoveryEnableBtn),
|
child: Text(context.lang.passwordlessRecoveryEnableBtn),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Text(
|
||||||
|
context.lang.backupTwonlySafeDesc,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: context.color.onSurfaceVariant,
|
||||||
|
fontSize: 13,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
|
||||||
|
if (userService.currentUser.isBackupEnabled) ...[
|
||||||
|
// 1. Identity tile
|
||||||
|
BetterListTile(
|
||||||
|
icon: FontAwesomeIcons.userCheck,
|
||||||
|
text: context.lang.backupIdentityHeader,
|
||||||
|
subtitle: Text(
|
||||||
|
_buildTileSubtitle(
|
||||||
|
_backupStatus?.identityLastSuccessFull,
|
||||||
|
_backupStatus?.identitySize,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// 2. Contacts & Messages tile
|
||||||
|
BetterListTile(
|
||||||
|
icon: FontAwesomeIcons.comments,
|
||||||
|
text: context.lang.backupArchiveHeader,
|
||||||
|
subtitle: Text(
|
||||||
|
_buildTileSubtitle(
|
||||||
|
_backupStatus?.archiveLastSuccessFull,
|
||||||
|
_backupStatus?.archiveSize,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// 3. Memories tile
|
||||||
|
BetterListTile(
|
||||||
|
icon: FontAwesomeIcons.photoFilm,
|
||||||
|
text: context.lang.memoriesBackupTitle,
|
||||||
|
subtitle: Text(
|
||||||
|
isFreePlan
|
||||||
|
? context.lang.backupMemoriesUpgradeRequired
|
||||||
|
: (!userService.currentUser.isCloudBackupEnabled
|
||||||
|
? context.lang.backupMemoriesNotEnabled
|
||||||
|
: (_memoriesUsage != null
|
||||||
|
? '${formatBytes(_memoriesUsage!.currentBytes.toInt())} / ${formatBytes(_memoriesUsage!.maxBytes.toInt())}'
|
||||||
|
: '-')),
|
||||||
|
),
|
||||||
|
trailing: Icon(
|
||||||
|
Icons.chevron_right_rounded,
|
||||||
|
color: context.color.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
onTap: () async {
|
||||||
|
if (isFreePlan) {
|
||||||
|
await context.push(Routes.settingsSubscription);
|
||||||
|
} else if (!userService
|
||||||
|
.currentUser
|
||||||
|
.isCloudBackupEnabled) {
|
||||||
|
await UserService.update(
|
||||||
|
(u) => u.isCloudBackupEnabled = true,
|
||||||
|
);
|
||||||
|
if (mounted) setState(() {});
|
||||||
|
unawaited(memoriesCloudService.checkUploads());
|
||||||
|
} else {
|
||||||
|
await context.navPush(const MemoriesBackupDetailView());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
|
||||||
Center(
|
Center(
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|
@ -212,9 +197,7 @@ class _BackupViewState extends State<BackupView> {
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
});
|
});
|
||||||
await BackupService.makeBackup(force: true);
|
await BackupService.makeBackup(force: true);
|
||||||
setState(() {
|
await _loadBackupStatus();
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
child: Text(context.lang.backupTwonlySaveNow),
|
child: Text(context.lang.backupTwonlySaveNow),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ class _SetupBackupViewState extends State<SetupBackupView> {
|
||||||
onTap: () => FocusScope.of(context).unfocus(),
|
onTap: () => FocusScope.of(context).unfocus(),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('twonly Backup'),
|
title: Text(context.lang.settingsBackup),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => showBackupExplanation(context),
|
onPressed: () => showBackupExplanation(context),
|
||||||
|
|
@ -127,12 +127,6 @@ class _SetupBackupViewState extends State<SetupBackupView> {
|
||||||
_repeatedController.text.isNotEmpty,
|
_repeatedController.text.isNotEmpty,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Text(
|
|
||||||
context.lang.backupNoPasswordRecovery,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: const TextStyle(fontSize: 12),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Center(
|
Center(
|
||||||
child: MyButton(
|
child: MyButton(
|
||||||
variant: MyButtonVariant.primaryMiddle,
|
variant: MyButtonVariant.primaryMiddle,
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ void showBackupExplanation(BuildContext context) {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
Text(
|
Text(
|
||||||
'twonly Backup',
|
context.lang.settingsBackup,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,312 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:drift/drift.dart' hide Column;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/locator.dart';
|
||||||
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart'
|
||||||
|
as server;
|
||||||
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
|
import 'package:twonly/src/services/memories/memories_cloud.service.dart';
|
||||||
|
import 'package:twonly/src/services/user.service.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/visual/components/snackbar.dart';
|
||||||
|
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
||||||
|
import 'package:twonly/src/visual/views/settings/data_and_storage/storage_contents.view.dart';
|
||||||
|
|
||||||
|
class MemoriesBackupDetailView extends StatefulWidget {
|
||||||
|
const MemoriesBackupDetailView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MemoriesBackupDetailView> createState() =>
|
||||||
|
_MemoriesBackupDetailViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MemoriesBackupDetailViewState extends State<MemoriesBackupDetailView> {
|
||||||
|
server.Response_MemoriesUsage? _memoriesUsage;
|
||||||
|
int _cloudOnlyCount = 0;
|
||||||
|
bool _isLoading = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_loadStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadStats() async {
|
||||||
|
final memoriesUsage = await apiService.getMemoriesUsage();
|
||||||
|
final cloudOnlyCount = await twonlyDB.mediaFilesDao
|
||||||
|
.getCloudOnlyMemoriesCount();
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_memoriesUsage = memoriesUsage;
|
||||||
|
_cloudOnlyCount = cloudOnlyCount;
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _disableBackup() async {
|
||||||
|
final confirmed = await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => Dialog(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(24),
|
||||||
|
),
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
context.lang.settingsStorageDisableBackupTitle,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: formattedText(
|
||||||
|
context,
|
||||||
|
context.lang.settingsStorageDisableBackupBody(
|
||||||
|
_cloudOnlyCount,
|
||||||
|
),
|
||||||
|
textColor: Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.onSurface.withValues(alpha: 0.8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(fontSize: 15),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: MyButton(
|
||||||
|
variant: MyButtonVariant.secondaryMiddle,
|
||||||
|
onPressed: () => Navigator.pop(context, false),
|
||||||
|
child: Text(context.lang.galleryCancel),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: MyButton(
|
||||||
|
variant: MyButtonVariant.errorMiddle,
|
||||||
|
onPressed: () => Navigator.pop(context, true),
|
||||||
|
child: Text(
|
||||||
|
context.lang.settingsStorageDisableBackupBtn,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (confirmed == true) {
|
||||||
|
try {
|
||||||
|
await apiService.disableMemoriesBackup();
|
||||||
|
final allMedias = await (twonlyDB.select(
|
||||||
|
twonlyDB.mediaFiles,
|
||||||
|
)..where((t) => t.stored.equals(true))).get();
|
||||||
|
for (final media in allMedias) {
|
||||||
|
final ms = MediaFileService(media);
|
||||||
|
if (!ms.storedPath.existsSync()) {
|
||||||
|
ms.fullMediaRemoval();
|
||||||
|
await twonlyDB.mediaFilesDao.deleteMediaFile(media.mediaId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await twonlyDB.mediaFilesDao.updateAllMediaFiles(
|
||||||
|
const MediaFilesCompanion(
|
||||||
|
cloudState: Value(CloudState.none),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await UserService.update((u) => u.isCloudBackupEnabled = false);
|
||||||
|
if (mounted) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (mounted) {
|
||||||
|
showSnackbar(context, e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(context.lang.memoriesBackupTitle),
|
||||||
|
),
|
||||||
|
body: _isLoading
|
||||||
|
? const Center(child: CircularProgressIndicator.adaptive())
|
||||||
|
: ListView(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
_memoriesUsage != null
|
||||||
|
? '${formatBytes(_memoriesUsage!.currentBytes.toInt())} / ${formatBytes(_memoriesUsage!.maxBytes.toInt())}'
|
||||||
|
: '-',
|
||||||
|
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
Container(
|
||||||
|
height: 24,
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.withValues(alpha: 0.2),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
if (_memoriesUsage == null ||
|
||||||
|
_memoriesUsage!.maxBytes == 0) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
final maxWidth = constraints.maxWidth;
|
||||||
|
final current = _memoriesUsage!.currentBytes.toDouble();
|
||||||
|
final max = _memoriesUsage!.maxBytes.toDouble();
|
||||||
|
final usageWidth =
|
||||||
|
((current / max).clamp(0.0, 1.0)) * maxWidth;
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
if (usageWidth > 0)
|
||||||
|
Container(
|
||||||
|
width: usageWidth,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
StreamBuilder<MemoriesBackupProgress?>(
|
||||||
|
initialData: memoriesCloudService.currentProgress,
|
||||||
|
stream: memoriesCloudService.progressStream,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final progress = snapshot.data;
|
||||||
|
final isSyncing =
|
||||||
|
progress != null && progress.totalPending > 0;
|
||||||
|
final percent = isSyncing
|
||||||
|
? ((progress.currentUploaded / progress.totalPending) +
|
||||||
|
(progress.currentUploadProgress /
|
||||||
|
progress.totalPending))
|
||||||
|
.clamp(0.0, 1.0)
|
||||||
|
: 0.0;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
if (isSyncing) ...[
|
||||||
|
Text(
|
||||||
|
'Syncing: ${progress.currentUploaded} / ${progress.totalPending} files (${(percent * 100).toStringAsFixed(1)}%)',
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
LinearProgressIndicator(
|
||||||
|
value: percent,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
],
|
||||||
|
Center(
|
||||||
|
child: MyButton(
|
||||||
|
variant: MyButtonVariant.primaryMiddle,
|
||||||
|
onPressed: () =>
|
||||||
|
context.navPush(const StorageContentsView()),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.folder_open_outlined,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(context.lang.settingsStorageContents),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Center(
|
||||||
|
child: MyButton(
|
||||||
|
variant: MyButtonVariant.secondaryDense,
|
||||||
|
onPressed: isSyncing
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
final memories = await twonlyDB
|
||||||
|
.mediaFilesDao
|
||||||
|
.getMemoriesToBackup();
|
||||||
|
if (memories.isEmpty) {
|
||||||
|
if (context.mounted) {
|
||||||
|
showSnackbar(
|
||||||
|
context,
|
||||||
|
context
|
||||||
|
.lang
|
||||||
|
.settingsStorageSyncUpToDate,
|
||||||
|
level: SnackbarLevel.success,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unawaited(
|
||||||
|
memoriesCloudService.checkUploads(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.sync, size: 18),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(context.lang.settingsStorageSyncNow),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Center(
|
||||||
|
child: MyButton(
|
||||||
|
variant: MyButtonVariant.secondaryDense,
|
||||||
|
onPressed: _disableBackup,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.cloud_off_outlined, size: 18),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
context
|
||||||
|
.lang
|
||||||
|
.settingsStorageDisableBackupAction,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,6 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:twonly/src/services/passwordless_recovery.service.dart';
|
import 'package:twonly/src/services/passwordless_recovery.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/elements/my_input.element.dart';
|
import 'package:twonly/src/visual/elements/my_input.element.dart';
|
||||||
import 'package:twonly/src/visual/themes/light.dart';
|
|
||||||
|
|
||||||
class _FactorOption {
|
class _FactorOption {
|
||||||
const _FactorOption({
|
const _FactorOption({
|
||||||
|
|
@ -113,7 +112,7 @@ class SecondFactorPicker extends StatelessWidget {
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected ? primaryColor : Colors.transparent,
|
color: isSelected ? context.color.primary : Colors.transparent,
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/themes/light.dart';
|
|
||||||
|
|
||||||
class ThresholdPicker extends StatelessWidget {
|
class ThresholdPicker extends StatelessWidget {
|
||||||
const ThresholdPicker({
|
const ThresholdPicker({
|
||||||
|
|
@ -103,7 +102,7 @@ class ThresholdPicker extends StatelessWidget {
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected ? primaryColor : Colors.transparent,
|
color: isSelected ? context.color.primary : Colors.transparent,
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,12 @@ class _ChatSettingsViewState extends State<ChatSettingsView> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> setShowRestoreFlame(bool value) async {
|
||||||
|
await UserService.update((u) {
|
||||||
|
u.showRestoreFlame = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|
@ -55,6 +61,13 @@ class _ChatSettingsViewState extends State<ChatSettingsView> {
|
||||||
.automaticallyMarkEqualMediaFilesAsOpened,
|
.automaticallyMarkEqualMediaFilesAsOpened,
|
||||||
onChanged: setAutomaticallyMarkEqualMediaFilesAsOpened,
|
onChanged: setAutomaticallyMarkEqualMediaFilesAsOpened,
|
||||||
),
|
),
|
||||||
|
SwitchListTile.adaptive(
|
||||||
|
title: Text(
|
||||||
|
context.lang.settingsShowRestoreFlameTitle,
|
||||||
|
),
|
||||||
|
value: userService.currentUser.showRestoreFlame,
|
||||||
|
onChanged: setShowRestoreFlame,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,12 @@
|
||||||
import 'dart:async';
|
|
||||||
import 'package:drift/drift.dart' hide Column;
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/constants/routes.keys.dart';
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart'
|
|
||||||
as server;
|
|
||||||
import 'package:twonly/src/providers/purchases.provider.dart';
|
import 'package:twonly/src/providers/purchases.provider.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
|
||||||
import 'package:twonly/src/services/memories/memories_cloud.service.dart';
|
|
||||||
import 'package:twonly/src/services/subscription.service.dart';
|
import 'package:twonly/src/services/subscription.service.dart';
|
||||||
import 'package:twonly/src/services/user.service.dart';
|
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/visual/components/snackbar.dart';
|
|
||||||
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
import 'package:twonly/src/visual/elements/my_button.element.dart';
|
||||||
import 'package:twonly/src/visual/views/settings/data_and_storage/storage_contents.view.dart';
|
import 'package:twonly/src/visual/views/settings/data_and_storage/storage_contents.view.dart';
|
||||||
|
|
||||||
|
|
@ -28,8 +19,6 @@ class ManageStorageView extends StatefulWidget {
|
||||||
|
|
||||||
class _ManageStorageViewState extends State<ManageStorageView> {
|
class _ManageStorageViewState extends State<ManageStorageView> {
|
||||||
Map<MediaType, int> _stats = {};
|
Map<MediaType, int> _stats = {};
|
||||||
server.Response_MemoriesUsage? _memoriesUsage;
|
|
||||||
int _cloudOnlyCount = 0;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -39,110 +28,13 @@ class _ManageStorageViewState extends State<ManageStorageView> {
|
||||||
|
|
||||||
Future<void> _loadStats() async {
|
Future<void> _loadStats() async {
|
||||||
final stats = await twonlyDB.mediaFilesDao.getStorageStats();
|
final stats = await twonlyDB.mediaFilesDao.getStorageStats();
|
||||||
final memoriesUsage = await apiService.getMemoriesUsage();
|
|
||||||
final cloudOnlyCount = await twonlyDB.mediaFilesDao
|
|
||||||
.getCloudOnlyMemoriesCount();
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_stats = stats;
|
_stats = stats;
|
||||||
_memoriesUsage = memoriesUsage;
|
|
||||||
_cloudOnlyCount = cloudOnlyCount;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _disableBackup() async {
|
|
||||||
final confirmed = await showDialog<bool>(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => Dialog(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(24),
|
|
||||||
),
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
context.lang.settingsStorageDisableBackupTitle,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 18,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text.rich(
|
|
||||||
TextSpan(
|
|
||||||
children: formattedText(
|
|
||||||
context,
|
|
||||||
context.lang.settingsStorageDisableBackupBody(
|
|
||||||
_cloudOnlyCount,
|
|
||||||
),
|
|
||||||
textColor: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.onSurface.withValues(alpha: 0.8),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: const TextStyle(fontSize: 15),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: MyButton(
|
|
||||||
variant: MyButtonVariant.secondaryMiddle,
|
|
||||||
onPressed: () => Navigator.pop(context, false),
|
|
||||||
child: Text(context.lang.galleryCancel),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: MyButton(
|
|
||||||
variant: MyButtonVariant.errorMiddle,
|
|
||||||
onPressed: () => Navigator.pop(context, true),
|
|
||||||
child: Text(context.lang.settingsStorageDisableBackupBtn),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (confirmed == true) {
|
|
||||||
try {
|
|
||||||
await apiService.disableMemoriesBackup();
|
|
||||||
final allMedias = await (twonlyDB.select(
|
|
||||||
twonlyDB.mediaFiles,
|
|
||||||
)..where((t) => t.stored.equals(true))).get();
|
|
||||||
for (final media in allMedias) {
|
|
||||||
final ms = MediaFileService(media);
|
|
||||||
if (!ms.storedPath.existsSync()) {
|
|
||||||
ms.fullMediaRemoval();
|
|
||||||
await twonlyDB.mediaFilesDao.deleteMediaFile(media.mediaId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await twonlyDB.mediaFilesDao.updateAllMediaFiles(
|
|
||||||
const MediaFilesCompanion(
|
|
||||||
cloudState: Value(CloudState.none),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await UserService.update((u) => u.isCloudBackupEnabled = false);
|
|
||||||
await _loadStats();
|
|
||||||
} catch (e) {
|
|
||||||
if (mounted) {
|
|
||||||
showSnackbar(context, e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final currentPlan = context.watch<PurchasesProvider>().plan;
|
final currentPlan = context.watch<PurchasesProvider>().plan;
|
||||||
|
|
@ -162,7 +54,7 @@ class _ManageStorageViewState extends State<ManageStorageView> {
|
||||||
body: ListView(
|
body: ListView(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
children: [
|
children: [
|
||||||
if (isFreePlan) ...[
|
if (isFreePlan || !userService.currentUser.isCloudBackupEnabled) ...[
|
||||||
Card(
|
Card(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
color: Theme.of(
|
color: Theme.of(
|
||||||
|
|
@ -177,223 +69,44 @@ class _ManageStorageViewState extends State<ManageStorageView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () => context.push(Routes.settingsSubscription),
|
onTap: () {
|
||||||
|
if (isFreePlan) {
|
||||||
|
context.push(Routes.settingsSubscription);
|
||||||
|
} else {
|
||||||
|
context.push(Routes.settingsBackup);
|
||||||
|
}
|
||||||
|
},
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Icons.cloud_off_outlined,
|
Icons.cloud_queue_rounded,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
size: 24,
|
size: 24,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Text(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
context.lang.backupFreeSpaceWithCloud,
|
||||||
children: [
|
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||||
Text(
|
|
||||||
context.lang.settingsStorageNoCloudBackupTitle,
|
|
||||||
style: Theme.of(context).textTheme.titleSmall
|
|
||||||
?.copyWith(
|
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
context.lang.settingsStorageNoCloudBackupCard,
|
|
||||||
style: Theme.of(context).textTheme.bodySmall
|
|
||||||
?.copyWith(
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
),
|
||||||
|
Icon(
|
||||||
|
Icons.chevron_right_rounded,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
] else ...[
|
|
||||||
Text(
|
|
||||||
context.lang.memoriesBackupTitle,
|
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
if (!userService.currentUser.isCloudBackupEnabled) ...[
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
context.lang.settingsStorageNoCloudBackupCard,
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium
|
|
||||||
?.copyWith(
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 16),
|
|
||||||
MyButton(
|
|
||||||
variant: MyButtonVariant.primaryMiddle,
|
|
||||||
onPressed: () async {
|
|
||||||
await UserService.update(
|
|
||||||
(u) => u.isCloudBackupEnabled = true,
|
|
||||||
);
|
|
||||||
setState(() {});
|
|
||||||
unawaited(memoriesCloudService.checkUploads());
|
|
||||||
},
|
|
||||||
child: Text(context.lang.enable),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
] else ...[
|
|
||||||
Text(
|
|
||||||
_memoriesUsage != null
|
|
||||||
? '${formatBytes(_memoriesUsage!.currentBytes.toInt())} / ${formatBytes(_memoriesUsage!.maxBytes.toInt())}'
|
|
||||||
: '-',
|
|
||||||
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
Container(
|
|
||||||
height: 24,
|
|
||||||
width: double.infinity,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.grey.withValues(alpha: 0.2),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
child: LayoutBuilder(
|
|
||||||
builder: (context, constraints) {
|
|
||||||
if (_memoriesUsage == null ||
|
|
||||||
_memoriesUsage!.maxBytes == 0) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
|
|
||||||
final maxWidth = constraints.maxWidth;
|
|
||||||
final current = _memoriesUsage!.currentBytes.toDouble();
|
|
||||||
final max = _memoriesUsage!.maxBytes.toDouble();
|
|
||||||
final usageWidth =
|
|
||||||
((current / max).clamp(0.0, 1.0)) * maxWidth;
|
|
||||||
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
if (usageWidth > 0)
|
|
||||||
Container(
|
|
||||||
width: usageWidth,
|
|
||||||
color: Colors.blue,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
StreamBuilder<MemoriesBackupProgress?>(
|
|
||||||
initialData: memoriesCloudService.currentProgress,
|
|
||||||
stream: memoriesCloudService.progressStream,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
final progress = snapshot.data;
|
|
||||||
final isSyncing =
|
|
||||||
progress != null && progress.totalPending > 0;
|
|
||||||
final percent = isSyncing
|
|
||||||
? ((progress.currentUploaded / progress.totalPending) +
|
|
||||||
(progress.currentUploadProgress /
|
|
||||||
progress.totalPending))
|
|
||||||
.clamp(0.0, 1.0)
|
|
||||||
: 0.0;
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
if (isSyncing) ...[
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
Text(
|
|
||||||
'Syncing: ${progress.currentUploaded} / ${progress.totalPending} files (${(percent * 100).toStringAsFixed(1)}%)',
|
|
||||||
style: const TextStyle(fontSize: 14),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 6),
|
|
||||||
LinearProgressIndicator(
|
|
||||||
value: percent,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Align(
|
|
||||||
child: MyButton(
|
|
||||||
variant: MyButtonVariant.primaryDense,
|
|
||||||
onPressed: isSyncing
|
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
final memories = await twonlyDB.mediaFilesDao
|
|
||||||
.getMemoriesToBackup();
|
|
||||||
if (memories.isEmpty) {
|
|
||||||
if (context.mounted) {
|
|
||||||
showSnackbar(
|
|
||||||
context,
|
|
||||||
context
|
|
||||||
.lang
|
|
||||||
.settingsStorageSyncUpToDate,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unawaited(
|
|
||||||
memoriesCloudService.checkUploads(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.sync, size: 18),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(context.lang.settingsStorageSyncNow),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
Align(
|
|
||||||
child: MyButton(
|
|
||||||
variant: MyButtonVariant.secondaryDense,
|
|
||||||
onPressed: _disableBackup,
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.cloud_off_outlined, size: 18),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
context.lang.settingsStorageDisableBackupAction,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
const Divider(),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
Text(
|
Text(
|
||||||
isFreePlan
|
isFreePlan
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue