mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-10-09 02:54:14 +00:00
improve games
This commit is contained in:
parent
57fa71af0c
commit
3e321ed53d
53 changed files with 3500 additions and 596 deletions
|
|
@ -12,11 +12,25 @@ Future<void> refreshCatalog() =>
|
|||
|
||||
/// What the in-app store offers, newest version of each app only.
|
||||
///
|
||||
/// `languages` is what the UI prefers, most preferred first. Descriptions are
|
||||
/// cached in every language the catalog carries and picked here, so which
|
||||
/// language a user reads is never sent anywhere.
|
||||
Future<List<WebxdcStoreApp>> catalog({required List<String> languages}) =>
|
||||
RustLib.instance.api.crateBridgeWebxdcCatalog(languages: languages);
|
||||
/// `languages` is what the UI prefers, most preferred first. Names and
|
||||
/// descriptions are cached in every language the catalog carries and picked
|
||||
/// here, so which language a user reads is never sent anywhere.
|
||||
Future<List<WebxdcStoreApp>> catalog({
|
||||
required String groupId,
|
||||
required List<String> languages,
|
||||
}) => RustLib.instance.api.crateBridgeWebxdcCatalog(
|
||||
groupId: groupId,
|
||||
languages: languages,
|
||||
);
|
||||
|
||||
/// One-time apps already placed into a chat, for the profile shortcut.
|
||||
Future<List<WebxdcOneTimeInstance>> oneTimeInstances({
|
||||
required String groupId,
|
||||
required List<String> languages,
|
||||
}) => RustLib.instance.api.crateBridgeWebxdcOneTimeInstances(
|
||||
groupId: groupId,
|
||||
languages: languages,
|
||||
);
|
||||
|
||||
/// Places an app into a chat and returns the id of the message that carries it.
|
||||
Future<String> createInstance({
|
||||
|
|
@ -32,12 +46,17 @@ Future<String> createInstance({
|
|||
Future<WebxdcInstanceInfo?> instance({required String instanceId}) =>
|
||||
RustLib.instance.api.crateBridgeWebxdcInstance(instanceId: instanceId);
|
||||
|
||||
/// Downloads and verifies the bundle if it is not already on disk, and returns
|
||||
/// where it landed. Called when the user starts an app, never on message
|
||||
/// Opens cached code, adopting an update only if already downloaded.
|
||||
/// Downloads and verifies the pinned bundle only when it is missing. Called when the user starts an app, never on message
|
||||
/// arrival: a message must not be able to make a device fetch anything.
|
||||
Future<String> prepareBundle({required String instanceId}) =>
|
||||
RustLib.instance.api.crateBridgeWebxdcPrepareBundle(instanceId: instanceId);
|
||||
|
||||
/// Background update check after a webxdc app closes. Downloads only; the
|
||||
/// running version changes on a later prepare_bundle call, never mid-session.
|
||||
Future<void> cacheUpdate({required String instanceId}) =>
|
||||
RustLib.instance.api.crateBridgeWebxdcCacheUpdate(instanceId: instanceId);
|
||||
|
||||
/// Answers one request the webview made for a file inside the bundle.
|
||||
///
|
||||
/// The platform shim has already established that the request came from this
|
||||
|
|
@ -65,6 +84,7 @@ Future<void> sendUpdate({
|
|||
String? href,
|
||||
String? summary,
|
||||
String? document,
|
||||
String? notify,
|
||||
}) => RustLib.instance.api.crateBridgeWebxdcSendUpdate(
|
||||
instanceId: instanceId,
|
||||
payload: payload,
|
||||
|
|
@ -72,6 +92,7 @@ Future<void> sendUpdate({
|
|||
href: href,
|
||||
summary: summary,
|
||||
document: document,
|
||||
notify: notify,
|
||||
);
|
||||
|
||||
/// The address the running app sees for a participant. Stable inside one
|
||||
|
|
@ -93,6 +114,10 @@ Future<String?> deleteInstance({required String instanceId}) => RustLib
|
|||
.api
|
||||
.crateBridgeWebxdcDeleteInstance(instanceId: instanceId);
|
||||
|
||||
/// Current members of the instance's chat; no account identifiers leave Rust.
|
||||
Future<String> members({required String instanceId}) =>
|
||||
RustLib.instance.api.crateBridgeWebxdcMembers(instanceId: instanceId);
|
||||
|
||||
class WebxdcInstanceInfo {
|
||||
final String instanceId;
|
||||
final String groupId;
|
||||
|
|
@ -140,6 +165,41 @@ class WebxdcInstanceInfo {
|
|||
document == other.document;
|
||||
}
|
||||
|
||||
class WebxdcOneTimeInstance {
|
||||
final String instanceId;
|
||||
final String name;
|
||||
final Uint8List? icon;
|
||||
final String? summary;
|
||||
final String? document;
|
||||
|
||||
const WebxdcOneTimeInstance({
|
||||
required this.instanceId,
|
||||
required this.name,
|
||||
this.icon,
|
||||
this.summary,
|
||||
this.document,
|
||||
});
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
instanceId.hashCode ^
|
||||
name.hashCode ^
|
||||
icon.hashCode ^
|
||||
summary.hashCode ^
|
||||
document.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is WebxdcOneTimeInstance &&
|
||||
runtimeType == other.runtimeType &&
|
||||
instanceId == other.instanceId &&
|
||||
name == other.name &&
|
||||
icon == other.icon &&
|
||||
summary == other.summary &&
|
||||
document == other.document;
|
||||
}
|
||||
|
||||
class WebxdcResponse {
|
||||
final int status;
|
||||
final String mime;
|
||||
|
|
@ -187,6 +247,11 @@ class WebxdcStoreApp {
|
|||
final String? sourceCodeUrl;
|
||||
final Uint8List? icon;
|
||||
final PlatformInt64 bundleBytes;
|
||||
final bool proOnly;
|
||||
final bool oneTime;
|
||||
|
||||
/// The existing instance in this chat when a one-time app was already placed.
|
||||
final String? instanceId;
|
||||
|
||||
const WebxdcStoreApp({
|
||||
required this.appId,
|
||||
|
|
@ -196,6 +261,9 @@ class WebxdcStoreApp {
|
|||
this.sourceCodeUrl,
|
||||
this.icon,
|
||||
required this.bundleBytes,
|
||||
required this.proOnly,
|
||||
required this.oneTime,
|
||||
this.instanceId,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -206,7 +274,10 @@ class WebxdcStoreApp {
|
|||
description.hashCode ^
|
||||
sourceCodeUrl.hashCode ^
|
||||
icon.hashCode ^
|
||||
bundleBytes.hashCode;
|
||||
bundleBytes.hashCode ^
|
||||
proOnly.hashCode ^
|
||||
oneTime.hashCode ^
|
||||
instanceId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
|
|
@ -219,7 +290,10 @@ class WebxdcStoreApp {
|
|||
description == other.description &&
|
||||
sourceCodeUrl == other.sourceCodeUrl &&
|
||||
icon == other.icon &&
|
||||
bundleBytes == other.bundleBytes;
|
||||
bundleBytes == other.bundleBytes &&
|
||||
proOnly == other.proOnly &&
|
||||
oneTime == other.oneTime &&
|
||||
instanceId == other.instanceId;
|
||||
}
|
||||
|
||||
class WebxdcUpdateEntry {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -238,6 +238,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
List<SqlValue> dco_decode_list_sql_value(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<WebxdcOneTimeInstance> dco_decode_list_webxdc_one_time_instance(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
List<WebxdcStoreApp> dco_decode_list_webxdc_store_app(dynamic raw);
|
||||
|
||||
|
|
@ -393,6 +398,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
WebxdcInstanceInfo dco_decode_webxdc_instance_info(dynamic raw);
|
||||
|
||||
@protected
|
||||
WebxdcOneTimeInstance dco_decode_webxdc_one_time_instance(dynamic raw);
|
||||
|
||||
@protected
|
||||
WebxdcResponse dco_decode_webxdc_response(dynamic raw);
|
||||
|
||||
|
|
@ -641,6 +649,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
List<SqlValue> sse_decode_list_sql_value(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<WebxdcOneTimeInstance> sse_decode_list_webxdc_one_time_instance(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
List<WebxdcStoreApp> sse_decode_list_webxdc_store_app(
|
||||
SseDeserializer deserializer,
|
||||
|
|
@ -820,6 +833,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
WebxdcOneTimeInstance sse_decode_webxdc_one_time_instance(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
WebxdcResponse sse_decode_webxdc_response(SseDeserializer deserializer);
|
||||
|
||||
|
|
@ -1143,6 +1161,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
void sse_encode_list_sql_value(List<SqlValue> self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_webxdc_one_time_instance(
|
||||
List<WebxdcOneTimeInstance> self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_webxdc_store_app(
|
||||
List<WebxdcStoreApp> self,
|
||||
|
|
@ -1362,6 +1386,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_webxdc_one_time_instance(
|
||||
WebxdcOneTimeInstance self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_webxdc_response(
|
||||
WebxdcResponse self,
|
||||
|
|
|
|||
|
|
@ -240,6 +240,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
List<SqlValue> dco_decode_list_sql_value(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<WebxdcOneTimeInstance> dco_decode_list_webxdc_one_time_instance(
|
||||
dynamic raw,
|
||||
);
|
||||
|
||||
@protected
|
||||
List<WebxdcStoreApp> dco_decode_list_webxdc_store_app(dynamic raw);
|
||||
|
||||
|
|
@ -395,6 +400,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
WebxdcInstanceInfo dco_decode_webxdc_instance_info(dynamic raw);
|
||||
|
||||
@protected
|
||||
WebxdcOneTimeInstance dco_decode_webxdc_one_time_instance(dynamic raw);
|
||||
|
||||
@protected
|
||||
WebxdcResponse dco_decode_webxdc_response(dynamic raw);
|
||||
|
||||
|
|
@ -643,6 +651,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
List<SqlValue> sse_decode_list_sql_value(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<WebxdcOneTimeInstance> sse_decode_list_webxdc_one_time_instance(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
List<WebxdcStoreApp> sse_decode_list_webxdc_store_app(
|
||||
SseDeserializer deserializer,
|
||||
|
|
@ -822,6 +835,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
WebxdcOneTimeInstance sse_decode_webxdc_one_time_instance(
|
||||
SseDeserializer deserializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
WebxdcResponse sse_decode_webxdc_response(SseDeserializer deserializer);
|
||||
|
||||
|
|
@ -1145,6 +1163,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
@protected
|
||||
void sse_encode_list_sql_value(List<SqlValue> self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_webxdc_one_time_instance(
|
||||
List<WebxdcOneTimeInstance> self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_webxdc_store_app(
|
||||
List<WebxdcStoreApp> self,
|
||||
|
|
@ -1364,6 +1388,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_webxdc_one_time_instance(
|
||||
WebxdcOneTimeInstance self,
|
||||
SseSerializer serializer,
|
||||
);
|
||||
|
||||
@protected
|
||||
void sse_encode_webxdc_response(
|
||||
WebxdcResponse self,
|
||||
|
|
|
|||
|
|
@ -641,8 +641,34 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
|||
return (delete(messages)..where((t) => t.messageId.equals(messageId))).go();
|
||||
}
|
||||
|
||||
Future<void> deleteMessagesByGroupId(String groupId) {
|
||||
return (delete(messages)..where((t) => t.groupId.equals(groupId))).go();
|
||||
Future<void> deleteMessagesByGroupId(String groupId) async {
|
||||
await transaction(() async {
|
||||
// A one-time app is the chat's durable shared object. Keep its card (and
|
||||
// therefore its instance and update log) while removing the visible chat
|
||||
// history and every ordinary app.
|
||||
await customStatement(
|
||||
'''
|
||||
DELETE FROM messages
|
||||
WHERE group_id = ?
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM webxdc_instances AS instance
|
||||
LEFT JOIN webxdc_apps AS app
|
||||
ON app.app_id = instance.app_id
|
||||
AND app.version = instance.version
|
||||
WHERE instance.instance_id = messages.message_id
|
||||
AND (app.one_time = 1 OR app.app_id IS NULL)
|
||||
)''',
|
||||
[groupId],
|
||||
);
|
||||
await customStatement(
|
||||
'''
|
||||
UPDATE groups
|
||||
SET deleted_content = 1, archived = 0, pinned = 0, draft_message = NULL
|
||||
WHERE group_id = ?''',
|
||||
[groupId],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
SingleOrNullSelectable<Message> getMessageById(String messageId) {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ class WebxdcApps extends Table {
|
|||
IntColumn get bundleBytes => integer()();
|
||||
BoolColumn get published => boolean().withDefault(const Constant(true))();
|
||||
IntColumn get cachedAt => integer()();
|
||||
BoolColumn get proOnly => boolean().withDefault(const Constant(false))();
|
||||
IntColumn get sortOrder => integer().withDefault(const Constant(0))();
|
||||
BoolColumn get oneTime => boolean().withDefault(const Constant(false))();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {appId, version};
|
||||
|
|
|
|||
|
|
@ -12663,6 +12663,48 @@ class $WebxdcAppsTable extends WebxdcApps
|
|||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
static const VerificationMeta _proOnlyMeta = const VerificationMeta(
|
||||
'proOnly',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<bool> proOnly = GeneratedColumn<bool>(
|
||||
'pro_only',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.bool,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("pro_only" IN (0, 1))',
|
||||
),
|
||||
defaultValue: const Constant(false),
|
||||
);
|
||||
static const VerificationMeta _sortOrderMeta = const VerificationMeta(
|
||||
'sortOrder',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<int> sortOrder = GeneratedColumn<int>(
|
||||
'sort_order',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: const Constant(0),
|
||||
);
|
||||
static const VerificationMeta _oneTimeMeta = const VerificationMeta(
|
||||
'oneTime',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<bool> oneTime = GeneratedColumn<bool>(
|
||||
'one_time',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.bool,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("one_time" IN (0, 1))',
|
||||
),
|
||||
defaultValue: const Constant(false),
|
||||
);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [
|
||||
appId,
|
||||
|
|
@ -12676,6 +12718,9 @@ class $WebxdcAppsTable extends WebxdcApps
|
|||
bundleBytes,
|
||||
published,
|
||||
cachedAt,
|
||||
proOnly,
|
||||
sortOrder,
|
||||
oneTime,
|
||||
];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
|
|
@ -12782,6 +12827,24 @@ class $WebxdcAppsTable extends WebxdcApps
|
|||
} else if (isInserting) {
|
||||
context.missing(_cachedAtMeta);
|
||||
}
|
||||
if (data.containsKey('pro_only')) {
|
||||
context.handle(
|
||||
_proOnlyMeta,
|
||||
proOnly.isAcceptableOrUnknown(data['pro_only']!, _proOnlyMeta),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('sort_order')) {
|
||||
context.handle(
|
||||
_sortOrderMeta,
|
||||
sortOrder.isAcceptableOrUnknown(data['sort_order']!, _sortOrderMeta),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('one_time')) {
|
||||
context.handle(
|
||||
_oneTimeMeta,
|
||||
oneTime.isAcceptableOrUnknown(data['one_time']!, _oneTimeMeta),
|
||||
);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
|
|
@ -12835,6 +12898,18 @@ class $WebxdcAppsTable extends WebxdcApps
|
|||
DriftSqlType.int,
|
||||
data['${effectivePrefix}cached_at'],
|
||||
)!,
|
||||
proOnly: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.bool,
|
||||
data['${effectivePrefix}pro_only'],
|
||||
)!,
|
||||
sortOrder: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}sort_order'],
|
||||
)!,
|
||||
oneTime: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.bool,
|
||||
data['${effectivePrefix}one_time'],
|
||||
)!,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -12866,6 +12941,9 @@ class WebxdcApp extends DataClass implements Insertable<WebxdcApp> {
|
|||
final int bundleBytes;
|
||||
final bool published;
|
||||
final int cachedAt;
|
||||
final bool proOnly;
|
||||
final int sortOrder;
|
||||
final bool oneTime;
|
||||
const WebxdcApp({
|
||||
required this.appId,
|
||||
required this.version,
|
||||
|
|
@ -12878,6 +12956,9 @@ class WebxdcApp extends DataClass implements Insertable<WebxdcApp> {
|
|||
required this.bundleBytes,
|
||||
required this.published,
|
||||
required this.cachedAt,
|
||||
required this.proOnly,
|
||||
required this.sortOrder,
|
||||
required this.oneTime,
|
||||
});
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
|
|
@ -12897,6 +12978,9 @@ class WebxdcApp extends DataClass implements Insertable<WebxdcApp> {
|
|||
map['bundle_bytes'] = Variable<int>(bundleBytes);
|
||||
map['published'] = Variable<bool>(published);
|
||||
map['cached_at'] = Variable<int>(cachedAt);
|
||||
map['pro_only'] = Variable<bool>(proOnly);
|
||||
map['sort_order'] = Variable<int>(sortOrder);
|
||||
map['one_time'] = Variable<bool>(oneTime);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
|
@ -12915,6 +12999,9 @@ class WebxdcApp extends DataClass implements Insertable<WebxdcApp> {
|
|||
bundleBytes: Value(bundleBytes),
|
||||
published: Value(published),
|
||||
cachedAt: Value(cachedAt),
|
||||
proOnly: Value(proOnly),
|
||||
sortOrder: Value(sortOrder),
|
||||
oneTime: Value(oneTime),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -12935,6 +13022,9 @@ class WebxdcApp extends DataClass implements Insertable<WebxdcApp> {
|
|||
bundleBytes: serializer.fromJson<int>(json['bundleBytes']),
|
||||
published: serializer.fromJson<bool>(json['published']),
|
||||
cachedAt: serializer.fromJson<int>(json['cachedAt']),
|
||||
proOnly: serializer.fromJson<bool>(json['proOnly']),
|
||||
sortOrder: serializer.fromJson<int>(json['sortOrder']),
|
||||
oneTime: serializer.fromJson<bool>(json['oneTime']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
|
|
@ -12952,6 +13042,9 @@ class WebxdcApp extends DataClass implements Insertable<WebxdcApp> {
|
|||
'bundleBytes': serializer.toJson<int>(bundleBytes),
|
||||
'published': serializer.toJson<bool>(published),
|
||||
'cachedAt': serializer.toJson<int>(cachedAt),
|
||||
'proOnly': serializer.toJson<bool>(proOnly),
|
||||
'sortOrder': serializer.toJson<int>(sortOrder),
|
||||
'oneTime': serializer.toJson<bool>(oneTime),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -12967,6 +13060,9 @@ class WebxdcApp extends DataClass implements Insertable<WebxdcApp> {
|
|||
int? bundleBytes,
|
||||
bool? published,
|
||||
int? cachedAt,
|
||||
bool? proOnly,
|
||||
int? sortOrder,
|
||||
bool? oneTime,
|
||||
}) => WebxdcApp(
|
||||
appId: appId ?? this.appId,
|
||||
version: version ?? this.version,
|
||||
|
|
@ -12981,6 +13077,9 @@ class WebxdcApp extends DataClass implements Insertable<WebxdcApp> {
|
|||
bundleBytes: bundleBytes ?? this.bundleBytes,
|
||||
published: published ?? this.published,
|
||||
cachedAt: cachedAt ?? this.cachedAt,
|
||||
proOnly: proOnly ?? this.proOnly,
|
||||
sortOrder: sortOrder ?? this.sortOrder,
|
||||
oneTime: oneTime ?? this.oneTime,
|
||||
);
|
||||
WebxdcApp copyWithCompanion(WebxdcAppsCompanion data) {
|
||||
return WebxdcApp(
|
||||
|
|
@ -13005,6 +13104,9 @@ class WebxdcApp extends DataClass implements Insertable<WebxdcApp> {
|
|||
: this.bundleBytes,
|
||||
published: data.published.present ? data.published.value : this.published,
|
||||
cachedAt: data.cachedAt.present ? data.cachedAt.value : this.cachedAt,
|
||||
proOnly: data.proOnly.present ? data.proOnly.value : this.proOnly,
|
||||
sortOrder: data.sortOrder.present ? data.sortOrder.value : this.sortOrder,
|
||||
oneTime: data.oneTime.present ? data.oneTime.value : this.oneTime,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -13021,7 +13123,10 @@ class WebxdcApp extends DataClass implements Insertable<WebxdcApp> {
|
|||
..write('bundleSha256: $bundleSha256, ')
|
||||
..write('bundleBytes: $bundleBytes, ')
|
||||
..write('published: $published, ')
|
||||
..write('cachedAt: $cachedAt')
|
||||
..write('cachedAt: $cachedAt, ')
|
||||
..write('proOnly: $proOnly, ')
|
||||
..write('sortOrder: $sortOrder, ')
|
||||
..write('oneTime: $oneTime')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
|
@ -13039,6 +13144,9 @@ class WebxdcApp extends DataClass implements Insertable<WebxdcApp> {
|
|||
bundleBytes,
|
||||
published,
|
||||
cachedAt,
|
||||
proOnly,
|
||||
sortOrder,
|
||||
oneTime,
|
||||
);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
|
|
@ -13054,7 +13162,10 @@ class WebxdcApp extends DataClass implements Insertable<WebxdcApp> {
|
|||
other.bundleSha256 == this.bundleSha256 &&
|
||||
other.bundleBytes == this.bundleBytes &&
|
||||
other.published == this.published &&
|
||||
other.cachedAt == this.cachedAt);
|
||||
other.cachedAt == this.cachedAt &&
|
||||
other.proOnly == this.proOnly &&
|
||||
other.sortOrder == this.sortOrder &&
|
||||
other.oneTime == this.oneTime);
|
||||
}
|
||||
|
||||
class WebxdcAppsCompanion extends UpdateCompanion<WebxdcApp> {
|
||||
|
|
@ -13069,6 +13180,9 @@ class WebxdcAppsCompanion extends UpdateCompanion<WebxdcApp> {
|
|||
final Value<int> bundleBytes;
|
||||
final Value<bool> published;
|
||||
final Value<int> cachedAt;
|
||||
final Value<bool> proOnly;
|
||||
final Value<int> sortOrder;
|
||||
final Value<bool> oneTime;
|
||||
final Value<int> rowid;
|
||||
const WebxdcAppsCompanion({
|
||||
this.appId = const Value.absent(),
|
||||
|
|
@ -13082,6 +13196,9 @@ class WebxdcAppsCompanion extends UpdateCompanion<WebxdcApp> {
|
|||
this.bundleBytes = const Value.absent(),
|
||||
this.published = const Value.absent(),
|
||||
this.cachedAt = const Value.absent(),
|
||||
this.proOnly = const Value.absent(),
|
||||
this.sortOrder = const Value.absent(),
|
||||
this.oneTime = const Value.absent(),
|
||||
this.rowid = const Value.absent(),
|
||||
});
|
||||
WebxdcAppsCompanion.insert({
|
||||
|
|
@ -13096,6 +13213,9 @@ class WebxdcAppsCompanion extends UpdateCompanion<WebxdcApp> {
|
|||
required int bundleBytes,
|
||||
this.published = const Value.absent(),
|
||||
required int cachedAt,
|
||||
this.proOnly = const Value.absent(),
|
||||
this.sortOrder = const Value.absent(),
|
||||
this.oneTime = const Value.absent(),
|
||||
this.rowid = const Value.absent(),
|
||||
}) : appId = Value(appId),
|
||||
version = Value(version),
|
||||
|
|
@ -13115,6 +13235,9 @@ class WebxdcAppsCompanion extends UpdateCompanion<WebxdcApp> {
|
|||
Expression<int>? bundleBytes,
|
||||
Expression<bool>? published,
|
||||
Expression<int>? cachedAt,
|
||||
Expression<bool>? proOnly,
|
||||
Expression<int>? sortOrder,
|
||||
Expression<bool>? oneTime,
|
||||
Expression<int>? rowid,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
|
|
@ -13129,6 +13252,9 @@ class WebxdcAppsCompanion extends UpdateCompanion<WebxdcApp> {
|
|||
if (bundleBytes != null) 'bundle_bytes': bundleBytes,
|
||||
if (published != null) 'published': published,
|
||||
if (cachedAt != null) 'cached_at': cachedAt,
|
||||
if (proOnly != null) 'pro_only': proOnly,
|
||||
if (sortOrder != null) 'sort_order': sortOrder,
|
||||
if (oneTime != null) 'one_time': oneTime,
|
||||
if (rowid != null) 'rowid': rowid,
|
||||
});
|
||||
}
|
||||
|
|
@ -13145,6 +13271,9 @@ class WebxdcAppsCompanion extends UpdateCompanion<WebxdcApp> {
|
|||
Value<int>? bundleBytes,
|
||||
Value<bool>? published,
|
||||
Value<int>? cachedAt,
|
||||
Value<bool>? proOnly,
|
||||
Value<int>? sortOrder,
|
||||
Value<bool>? oneTime,
|
||||
Value<int>? rowid,
|
||||
}) {
|
||||
return WebxdcAppsCompanion(
|
||||
|
|
@ -13159,6 +13288,9 @@ class WebxdcAppsCompanion extends UpdateCompanion<WebxdcApp> {
|
|||
bundleBytes: bundleBytes ?? this.bundleBytes,
|
||||
published: published ?? this.published,
|
||||
cachedAt: cachedAt ?? this.cachedAt,
|
||||
proOnly: proOnly ?? this.proOnly,
|
||||
sortOrder: sortOrder ?? this.sortOrder,
|
||||
oneTime: oneTime ?? this.oneTime,
|
||||
rowid: rowid ?? this.rowid,
|
||||
);
|
||||
}
|
||||
|
|
@ -13199,6 +13331,15 @@ class WebxdcAppsCompanion extends UpdateCompanion<WebxdcApp> {
|
|||
if (cachedAt.present) {
|
||||
map['cached_at'] = Variable<int>(cachedAt.value);
|
||||
}
|
||||
if (proOnly.present) {
|
||||
map['pro_only'] = Variable<bool>(proOnly.value);
|
||||
}
|
||||
if (sortOrder.present) {
|
||||
map['sort_order'] = Variable<int>(sortOrder.value);
|
||||
}
|
||||
if (oneTime.present) {
|
||||
map['one_time'] = Variable<bool>(oneTime.value);
|
||||
}
|
||||
if (rowid.present) {
|
||||
map['rowid'] = Variable<int>(rowid.value);
|
||||
}
|
||||
|
|
@ -13219,6 +13360,9 @@ class WebxdcAppsCompanion extends UpdateCompanion<WebxdcApp> {
|
|||
..write('bundleBytes: $bundleBytes, ')
|
||||
..write('published: $published, ')
|
||||
..write('cachedAt: $cachedAt, ')
|
||||
..write('proOnly: $proOnly, ')
|
||||
..write('sortOrder: $sortOrder, ')
|
||||
..write('oneTime: $oneTime, ')
|
||||
..write('rowid: $rowid')
|
||||
..write(')'))
|
||||
.toString();
|
||||
|
|
@ -25803,6 +25947,9 @@ typedef $$WebxdcAppsTableCreateCompanionBuilder =
|
|||
required int bundleBytes,
|
||||
Value<bool> published,
|
||||
required int cachedAt,
|
||||
Value<bool> proOnly,
|
||||
Value<int> sortOrder,
|
||||
Value<bool> oneTime,
|
||||
Value<int> rowid,
|
||||
});
|
||||
typedef $$WebxdcAppsTableUpdateCompanionBuilder =
|
||||
|
|
@ -25818,6 +25965,9 @@ typedef $$WebxdcAppsTableUpdateCompanionBuilder =
|
|||
Value<int> bundleBytes,
|
||||
Value<bool> published,
|
||||
Value<int> cachedAt,
|
||||
Value<bool> proOnly,
|
||||
Value<int> sortOrder,
|
||||
Value<bool> oneTime,
|
||||
Value<int> rowid,
|
||||
});
|
||||
|
||||
|
|
@ -25884,6 +26034,21 @@ class $$WebxdcAppsTableFilterComposer
|
|||
column: $table.cachedAt,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<bool> get proOnly => $composableBuilder(
|
||||
column: $table.proOnly,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<int> get sortOrder => $composableBuilder(
|
||||
column: $table.sortOrder,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<bool> get oneTime => $composableBuilder(
|
||||
column: $table.oneTime,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$WebxdcAppsTableOrderingComposer
|
||||
|
|
@ -25949,6 +26114,21 @@ class $$WebxdcAppsTableOrderingComposer
|
|||
column: $table.cachedAt,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<bool> get proOnly => $composableBuilder(
|
||||
column: $table.proOnly,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<int> get sortOrder => $composableBuilder(
|
||||
column: $table.sortOrder,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<bool> get oneTime => $composableBuilder(
|
||||
column: $table.oneTime,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$WebxdcAppsTableAnnotationComposer
|
||||
|
|
@ -26002,6 +26182,15 @@ class $$WebxdcAppsTableAnnotationComposer
|
|||
|
||||
GeneratedColumn<int> get cachedAt =>
|
||||
$composableBuilder(column: $table.cachedAt, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<bool> get proOnly =>
|
||||
$composableBuilder(column: $table.proOnly, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<int> get sortOrder =>
|
||||
$composableBuilder(column: $table.sortOrder, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<bool> get oneTime =>
|
||||
$composableBuilder(column: $table.oneTime, builder: (column) => column);
|
||||
}
|
||||
|
||||
class $$WebxdcAppsTableTableManager
|
||||
|
|
@ -26043,6 +26232,9 @@ class $$WebxdcAppsTableTableManager
|
|||
Value<int> bundleBytes = const Value.absent(),
|
||||
Value<bool> published = const Value.absent(),
|
||||
Value<int> cachedAt = const Value.absent(),
|
||||
Value<bool> proOnly = const Value.absent(),
|
||||
Value<int> sortOrder = const Value.absent(),
|
||||
Value<bool> oneTime = const Value.absent(),
|
||||
Value<int> rowid = const Value.absent(),
|
||||
}) => WebxdcAppsCompanion(
|
||||
appId: appId,
|
||||
|
|
@ -26056,6 +26248,9 @@ class $$WebxdcAppsTableTableManager
|
|||
bundleBytes: bundleBytes,
|
||||
published: published,
|
||||
cachedAt: cachedAt,
|
||||
proOnly: proOnly,
|
||||
sortOrder: sortOrder,
|
||||
oneTime: oneTime,
|
||||
rowid: rowid,
|
||||
),
|
||||
createCompanionCallback:
|
||||
|
|
@ -26071,6 +26266,9 @@ class $$WebxdcAppsTableTableManager
|
|||
required int bundleBytes,
|
||||
Value<bool> published = const Value.absent(),
|
||||
required int cachedAt,
|
||||
Value<bool> proOnly = const Value.absent(),
|
||||
Value<int> sortOrder = const Value.absent(),
|
||||
Value<bool> oneTime = const Value.absent(),
|
||||
Value<int> rowid = const Value.absent(),
|
||||
}) => WebxdcAppsCompanion.insert(
|
||||
appId: appId,
|
||||
|
|
@ -26084,6 +26282,9 @@ class $$WebxdcAppsTableTableManager
|
|||
bundleBytes: bundleBytes,
|
||||
published: published,
|
||||
cachedAt: cachedAt,
|
||||
proOnly: proOnly,
|
||||
sortOrder: sortOrder,
|
||||
oneTime: oneTime,
|
||||
rowid: rowid,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
|
|
|
|||
|
|
@ -98,6 +98,12 @@ abstract class AppLocalizations {
|
|||
Locale('en'),
|
||||
];
|
||||
|
||||
/// No description provided for @subscriptionAllApps.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Access to all apps'**
|
||||
String get subscriptionAllApps;
|
||||
|
||||
/// No description provided for @registerSlogan.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
@ -4736,6 +4742,30 @@ abstract class AppLocalizations {
|
|||
/// **'The app could not be added to this chat.'**
|
||||
String get webxdcStoreFailed;
|
||||
|
||||
/// No description provided for @webxdcStoreAlreadyAdded.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Already added to this chat'**
|
||||
String get webxdcStoreAlreadyAdded;
|
||||
|
||||
/// No description provided for @webxdcChatAppsMenu.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Chat apps'**
|
||||
String get webxdcChatAppsMenu;
|
||||
|
||||
/// No description provided for @webxdcChatAppsTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Chat apps'**
|
||||
String get webxdcChatAppsTitle;
|
||||
|
||||
/// No description provided for @webxdcChatAppsEmpty.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No one-time apps have been added to this chat yet.'**
|
||||
String get webxdcChatAppsEmpty;
|
||||
|
||||
/// No description provided for @webxdcUnavailable.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ import 'app_localizations.dart';
|
|||
class AppLocalizationsDe extends AppLocalizations {
|
||||
AppLocalizationsDe([String locale = 'de']) : super(locale);
|
||||
|
||||
@override
|
||||
String get subscriptionAllApps => 'Zugriff auf alle Apps';
|
||||
|
||||
@override
|
||||
String get registerSlogan => 'Privat in Kontakt bleiben.';
|
||||
|
||||
|
|
@ -2754,6 +2757,19 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
String get webxdcStoreFailed =>
|
||||
'Die App konnte diesem Chat nicht hinzugefügt werden.';
|
||||
|
||||
@override
|
||||
String get webxdcStoreAlreadyAdded => 'Bereits zu diesem Chat hinzugefügt';
|
||||
|
||||
@override
|
||||
String get webxdcChatAppsMenu => 'Chat-Apps';
|
||||
|
||||
@override
|
||||
String get webxdcChatAppsTitle => 'Chat-Apps';
|
||||
|
||||
@override
|
||||
String get webxdcChatAppsEmpty =>
|
||||
'Diesem Chat wurden noch keine einmaligen Apps hinzugefügt.';
|
||||
|
||||
@override
|
||||
String get webxdcUnavailable => 'Diese App konnte nicht geladen werden.';
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ import 'app_localizations.dart';
|
|||
class AppLocalizationsEn extends AppLocalizations {
|
||||
AppLocalizationsEn([String locale = 'en']) : super(locale);
|
||||
|
||||
@override
|
||||
String get subscriptionAllApps => 'Access to all apps';
|
||||
|
||||
@override
|
||||
String get registerSlogan => 'Stay in touch privately.';
|
||||
|
||||
|
|
@ -2726,6 +2729,19 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get webxdcStoreFailed => 'The app could not be added to this chat.';
|
||||
|
||||
@override
|
||||
String get webxdcStoreAlreadyAdded => 'Already added to this chat';
|
||||
|
||||
@override
|
||||
String get webxdcChatAppsMenu => 'Chat apps';
|
||||
|
||||
@override
|
||||
String get webxdcChatAppsTitle => 'Chat apps';
|
||||
|
||||
@override
|
||||
String get webxdcChatAppsEmpty =>
|
||||
'No one-time apps have been added to this chat yet.';
|
||||
|
||||
@override
|
||||
String get webxdcUnavailable => 'This app could not be loaded.';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"subscriptionAllApps": "Zugriff auf alle Apps",
|
||||
"@@locale": "de",
|
||||
"registerSlogan": "Privat in Kontakt bleiben.",
|
||||
"onboardingWelcomeTitle": "Willkommen bei twonly!",
|
||||
|
|
@ -1037,6 +1038,10 @@
|
|||
"webxdcStoreEmpty": "Es sind noch keine Apps verfügbar.",
|
||||
"webxdcStoreOffline": "Die App-Liste konnte nicht geladen werden. Prüfe deine Verbindung und versuche es erneut.",
|
||||
"webxdcStoreFailed": "Die App konnte diesem Chat nicht hinzugefügt werden.",
|
||||
"webxdcStoreAlreadyAdded": "Bereits zu diesem Chat hinzugefügt",
|
||||
"webxdcChatAppsMenu": "Chat-Apps",
|
||||
"webxdcChatAppsTitle": "Chat-Apps",
|
||||
"webxdcChatAppsEmpty": "Diesem Chat wurden noch keine einmaligen Apps hinzugefügt.",
|
||||
"webxdcUnavailable": "Diese App konnte nicht geladen werden.",
|
||||
"webxdcDeleteConfirm": "Alles, was diese App auf diesem Gerät gespeichert hat, wird gelöscht. Andere Mitglieder behalten ihre eigene Kopie.",
|
||||
"webxdcExternalLinkTitle": "twonly verlassen?",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"subscriptionAllApps": "Access to all apps",
|
||||
"@@locale": "en",
|
||||
"registerSlogan": "Stay in touch privately.",
|
||||
"onboardingWelcomeTitle": "Welcome to twonly!",
|
||||
|
|
@ -1047,6 +1048,10 @@
|
|||
"webxdcStoreEmpty": "No apps are available yet.",
|
||||
"webxdcStoreOffline": "The app list could not be loaded. Check your connection and try again.",
|
||||
"webxdcStoreFailed": "The app could not be added to this chat.",
|
||||
"webxdcStoreAlreadyAdded": "Already added to this chat",
|
||||
"webxdcChatAppsMenu": "Chat apps",
|
||||
"webxdcChatAppsTitle": "Chat apps",
|
||||
"webxdcChatAppsEmpty": "No one-time apps have been added to this chat yet.",
|
||||
"webxdcUnavailable": "This app could not be loaded.",
|
||||
"webxdcDeleteConfirm": "Everything this app saved on this device is deleted. Other members keep their own copy.",
|
||||
"webxdcExternalLinkTitle": "Leave twonly?",
|
||||
|
|
|
|||
|
|
@ -109,6 +109,8 @@ class AdditionalMessageData extends $pb.GeneratedMessage {
|
|||
WebxdcApp? webxdcApp,
|
||||
WebxdcUpdate? webxdcUpdate,
|
||||
WebxdcOrigin? webxdcOrigin,
|
||||
WebxdcSyncChunk? webxdcSync,
|
||||
WebxdcSyncRequest? webxdcSyncRequest,
|
||||
}) {
|
||||
final result = create();
|
||||
if (type != null) result.type = type;
|
||||
|
|
@ -120,6 +122,8 @@ class AdditionalMessageData extends $pb.GeneratedMessage {
|
|||
if (webxdcApp != null) result.webxdcApp = webxdcApp;
|
||||
if (webxdcUpdate != null) result.webxdcUpdate = webxdcUpdate;
|
||||
if (webxdcOrigin != null) result.webxdcOrigin = webxdcOrigin;
|
||||
if (webxdcSync != null) result.webxdcSync = webxdcSync;
|
||||
if (webxdcSyncRequest != null) result.webxdcSyncRequest = webxdcSyncRequest;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -148,6 +152,10 @@ class AdditionalMessageData extends $pb.GeneratedMessage {
|
|||
subBuilder: WebxdcUpdate.create)
|
||||
..aOM<WebxdcOrigin>(8, _omitFieldNames ? '' : 'webxdcOrigin',
|
||||
subBuilder: WebxdcOrigin.create)
|
||||
..aOM<WebxdcSyncChunk>(9, _omitFieldNames ? '' : 'webxdcSync',
|
||||
subBuilder: WebxdcSyncChunk.create)
|
||||
..aOM<WebxdcSyncRequest>(10, _omitFieldNames ? '' : 'webxdcSyncRequest',
|
||||
subBuilder: WebxdcSyncRequest.create)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
|
|
@ -241,6 +249,464 @@ class AdditionalMessageData extends $pb.GeneratedMessage {
|
|||
void clearWebxdcOrigin() => $_clearField(8);
|
||||
@$pb.TagNumber(8)
|
||||
WebxdcOrigin ensureWebxdcOrigin() => $_ensure(7);
|
||||
|
||||
@$pb.TagNumber(9)
|
||||
WebxdcSyncChunk get webxdcSync => $_getN(8);
|
||||
@$pb.TagNumber(9)
|
||||
set webxdcSync(WebxdcSyncChunk value) => $_setField(9, value);
|
||||
@$pb.TagNumber(9)
|
||||
$core.bool hasWebxdcSync() => $_has(8);
|
||||
@$pb.TagNumber(9)
|
||||
void clearWebxdcSync() => $_clearField(9);
|
||||
@$pb.TagNumber(9)
|
||||
WebxdcSyncChunk ensureWebxdcSync() => $_ensure(8);
|
||||
|
||||
@$pb.TagNumber(10)
|
||||
WebxdcSyncRequest get webxdcSyncRequest => $_getN(9);
|
||||
@$pb.TagNumber(10)
|
||||
set webxdcSyncRequest(WebxdcSyncRequest value) => $_setField(10, value);
|
||||
@$pb.TagNumber(10)
|
||||
$core.bool hasWebxdcSyncRequest() => $_has(9);
|
||||
@$pb.TagNumber(10)
|
||||
void clearWebxdcSyncRequest() => $_clearField(10);
|
||||
@$pb.TagNumber(10)
|
||||
WebxdcSyncRequest ensureWebxdcSyncRequest() => $_ensure(9);
|
||||
}
|
||||
|
||||
/// A peer-to-peer transfer of existing one-time app state. The encoded payload
|
||||
/// is split into bounded hidden messages and installed only after every chunk
|
||||
/// arrived and the digest matches.
|
||||
class WebxdcSyncChunk extends $pb.GeneratedMessage {
|
||||
factory WebxdcSyncChunk({
|
||||
$core.String? transferId,
|
||||
$core.int? chunkIndex,
|
||||
$core.int? chunkCount,
|
||||
$core.List<$core.int>? payloadSha256,
|
||||
$core.List<$core.int>? payload,
|
||||
}) {
|
||||
final result = create();
|
||||
if (transferId != null) result.transferId = transferId;
|
||||
if (chunkIndex != null) result.chunkIndex = chunkIndex;
|
||||
if (chunkCount != null) result.chunkCount = chunkCount;
|
||||
if (payloadSha256 != null) result.payloadSha256 = payloadSha256;
|
||||
if (payload != null) result.payload = payload;
|
||||
return result;
|
||||
}
|
||||
|
||||
WebxdcSyncChunk._();
|
||||
|
||||
factory WebxdcSyncChunk.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory WebxdcSyncChunk.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'WebxdcSyncChunk',
|
||||
createEmptyInstance: create)
|
||||
..aOS(1, _omitFieldNames ? '' : 'transferId')
|
||||
..aI(2, _omitFieldNames ? '' : 'chunkIndex', fieldType: $pb.PbFieldType.OU3)
|
||||
..aI(3, _omitFieldNames ? '' : 'chunkCount', fieldType: $pb.PbFieldType.OU3)
|
||||
..a<$core.List<$core.int>>(
|
||||
4, _omitFieldNames ? '' : 'payloadSha256', $pb.PbFieldType.OY)
|
||||
..a<$core.List<$core.int>>(
|
||||
5, _omitFieldNames ? '' : 'payload', $pb.PbFieldType.OY)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
WebxdcSyncChunk clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
WebxdcSyncChunk copyWith(void Function(WebxdcSyncChunk) updates) =>
|
||||
super.copyWith((message) => updates(message as WebxdcSyncChunk))
|
||||
as WebxdcSyncChunk;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static WebxdcSyncChunk create() => WebxdcSyncChunk._();
|
||||
@$core.override
|
||||
WebxdcSyncChunk createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static WebxdcSyncChunk getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<WebxdcSyncChunk>(create);
|
||||
static WebxdcSyncChunk? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get transferId => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set transferId($core.String value) => $_setString(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasTransferId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearTransferId() => $_clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.int get chunkIndex => $_getIZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set chunkIndex($core.int value) => $_setUnsignedInt32(1, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasChunkIndex() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearChunkIndex() => $_clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.int get chunkCount => $_getIZ(2);
|
||||
@$pb.TagNumber(3)
|
||||
set chunkCount($core.int value) => $_setUnsignedInt32(2, value);
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasChunkCount() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearChunkCount() => $_clearField(3);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.List<$core.int> get payloadSha256 => $_getN(3);
|
||||
@$pb.TagNumber(4)
|
||||
set payloadSha256($core.List<$core.int> value) => $_setBytes(3, value);
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasPayloadSha256() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearPayloadSha256() => $_clearField(4);
|
||||
|
||||
@$pb.TagNumber(5)
|
||||
$core.List<$core.int> get payload => $_getN(4);
|
||||
@$pb.TagNumber(5)
|
||||
set payload($core.List<$core.int> value) => $_setBytes(4, value);
|
||||
@$pb.TagNumber(5)
|
||||
$core.bool hasPayload() => $_has(4);
|
||||
@$pb.TagNumber(5)
|
||||
void clearPayload() => $_clearField(5);
|
||||
}
|
||||
|
||||
class WebxdcSyncRequest extends $pb.GeneratedMessage {
|
||||
factory WebxdcSyncRequest() => create();
|
||||
|
||||
WebxdcSyncRequest._();
|
||||
|
||||
factory WebxdcSyncRequest.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory WebxdcSyncRequest.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'WebxdcSyncRequest',
|
||||
createEmptyInstance: create)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
WebxdcSyncRequest clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
WebxdcSyncRequest copyWith(void Function(WebxdcSyncRequest) updates) =>
|
||||
super.copyWith((message) => updates(message as WebxdcSyncRequest))
|
||||
as WebxdcSyncRequest;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static WebxdcSyncRequest create() => WebxdcSyncRequest._();
|
||||
@$core.override
|
||||
WebxdcSyncRequest createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static WebxdcSyncRequest getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<WebxdcSyncRequest>(create);
|
||||
static WebxdcSyncRequest? _defaultInstance;
|
||||
}
|
||||
|
||||
class WebxdcSyncPayload extends $pb.GeneratedMessage {
|
||||
factory WebxdcSyncPayload({
|
||||
$core.Iterable<WebxdcSyncInstance>? instances,
|
||||
}) {
|
||||
final result = create();
|
||||
if (instances != null) result.instances.addAll(instances);
|
||||
return result;
|
||||
}
|
||||
|
||||
WebxdcSyncPayload._();
|
||||
|
||||
factory WebxdcSyncPayload.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory WebxdcSyncPayload.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'WebxdcSyncPayload',
|
||||
createEmptyInstance: create)
|
||||
..pPM<WebxdcSyncInstance>(1, _omitFieldNames ? '' : 'instances',
|
||||
subBuilder: WebxdcSyncInstance.create)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
WebxdcSyncPayload clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
WebxdcSyncPayload copyWith(void Function(WebxdcSyncPayload) updates) =>
|
||||
super.copyWith((message) => updates(message as WebxdcSyncPayload))
|
||||
as WebxdcSyncPayload;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static WebxdcSyncPayload create() => WebxdcSyncPayload._();
|
||||
@$core.override
|
||||
WebxdcSyncPayload createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static WebxdcSyncPayload getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<WebxdcSyncPayload>(create);
|
||||
static WebxdcSyncPayload? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$pb.PbList<WebxdcSyncInstance> get instances => $_getList(0);
|
||||
}
|
||||
|
||||
class WebxdcSyncInstance extends $pb.GeneratedMessage {
|
||||
factory WebxdcSyncInstance({
|
||||
$core.String? instanceId,
|
||||
$core.String? appId,
|
||||
$fixnum.Int64? version,
|
||||
$core.String? summary,
|
||||
$core.String? document,
|
||||
$fixnum.Int64? createdAt,
|
||||
$fixnum.Int64? lastUpdateAt,
|
||||
$core.Iterable<WebxdcSyncUpdate>? updates,
|
||||
}) {
|
||||
final result = create();
|
||||
if (instanceId != null) result.instanceId = instanceId;
|
||||
if (appId != null) result.appId = appId;
|
||||
if (version != null) result.version = version;
|
||||
if (summary != null) result.summary = summary;
|
||||
if (document != null) result.document = document;
|
||||
if (createdAt != null) result.createdAt = createdAt;
|
||||
if (lastUpdateAt != null) result.lastUpdateAt = lastUpdateAt;
|
||||
if (updates != null) result.updates.addAll(updates);
|
||||
return result;
|
||||
}
|
||||
|
||||
WebxdcSyncInstance._();
|
||||
|
||||
factory WebxdcSyncInstance.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory WebxdcSyncInstance.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'WebxdcSyncInstance',
|
||||
createEmptyInstance: create)
|
||||
..aOS(1, _omitFieldNames ? '' : 'instanceId')
|
||||
..aOS(2, _omitFieldNames ? '' : 'appId')
|
||||
..aInt64(3, _omitFieldNames ? '' : 'version')
|
||||
..aOS(4, _omitFieldNames ? '' : 'summary')
|
||||
..aOS(5, _omitFieldNames ? '' : 'document')
|
||||
..aInt64(6, _omitFieldNames ? '' : 'createdAt')
|
||||
..aInt64(7, _omitFieldNames ? '' : 'lastUpdateAt')
|
||||
..pPM<WebxdcSyncUpdate>(8, _omitFieldNames ? '' : 'updates',
|
||||
subBuilder: WebxdcSyncUpdate.create)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
WebxdcSyncInstance clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
WebxdcSyncInstance copyWith(void Function(WebxdcSyncInstance) updates) =>
|
||||
super.copyWith((message) => updates(message as WebxdcSyncInstance))
|
||||
as WebxdcSyncInstance;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static WebxdcSyncInstance create() => WebxdcSyncInstance._();
|
||||
@$core.override
|
||||
WebxdcSyncInstance createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static WebxdcSyncInstance getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<WebxdcSyncInstance>(create);
|
||||
static WebxdcSyncInstance? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get instanceId => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set instanceId($core.String value) => $_setString(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasInstanceId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearInstanceId() => $_clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$core.String get appId => $_getSZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set appId($core.String value) => $_setString(1, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasAppId() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearAppId() => $_clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$fixnum.Int64 get version => $_getI64(2);
|
||||
@$pb.TagNumber(3)
|
||||
set version($fixnum.Int64 value) => $_setInt64(2, value);
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasVersion() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearVersion() => $_clearField(3);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.String get summary => $_getSZ(3);
|
||||
@$pb.TagNumber(4)
|
||||
set summary($core.String value) => $_setString(3, value);
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasSummary() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearSummary() => $_clearField(4);
|
||||
|
||||
@$pb.TagNumber(5)
|
||||
$core.String get document => $_getSZ(4);
|
||||
@$pb.TagNumber(5)
|
||||
set document($core.String value) => $_setString(4, value);
|
||||
@$pb.TagNumber(5)
|
||||
$core.bool hasDocument() => $_has(4);
|
||||
@$pb.TagNumber(5)
|
||||
void clearDocument() => $_clearField(5);
|
||||
|
||||
@$pb.TagNumber(6)
|
||||
$fixnum.Int64 get createdAt => $_getI64(5);
|
||||
@$pb.TagNumber(6)
|
||||
set createdAt($fixnum.Int64 value) => $_setInt64(5, value);
|
||||
@$pb.TagNumber(6)
|
||||
$core.bool hasCreatedAt() => $_has(5);
|
||||
@$pb.TagNumber(6)
|
||||
void clearCreatedAt() => $_clearField(6);
|
||||
|
||||
@$pb.TagNumber(7)
|
||||
$fixnum.Int64 get lastUpdateAt => $_getI64(6);
|
||||
@$pb.TagNumber(7)
|
||||
set lastUpdateAt($fixnum.Int64 value) => $_setInt64(6, value);
|
||||
@$pb.TagNumber(7)
|
||||
$core.bool hasLastUpdateAt() => $_has(6);
|
||||
@$pb.TagNumber(7)
|
||||
void clearLastUpdateAt() => $_clearField(7);
|
||||
|
||||
@$pb.TagNumber(8)
|
||||
$pb.PbList<WebxdcSyncUpdate> get updates => $_getList(7);
|
||||
}
|
||||
|
||||
class WebxdcSyncUpdate extends $pb.GeneratedMessage {
|
||||
factory WebxdcSyncUpdate({
|
||||
$core.String? messageId,
|
||||
$fixnum.Int64? senderId,
|
||||
$core.String? payload,
|
||||
$core.String? info,
|
||||
$core.String? href,
|
||||
$fixnum.Int64? receivedAt,
|
||||
}) {
|
||||
final result = create();
|
||||
if (messageId != null) result.messageId = messageId;
|
||||
if (senderId != null) result.senderId = senderId;
|
||||
if (payload != null) result.payload = payload;
|
||||
if (info != null) result.info = info;
|
||||
if (href != null) result.href = href;
|
||||
if (receivedAt != null) result.receivedAt = receivedAt;
|
||||
return result;
|
||||
}
|
||||
|
||||
WebxdcSyncUpdate._();
|
||||
|
||||
factory WebxdcSyncUpdate.fromBuffer($core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory WebxdcSyncUpdate.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'WebxdcSyncUpdate',
|
||||
createEmptyInstance: create)
|
||||
..aOS(1, _omitFieldNames ? '' : 'messageId')
|
||||
..aInt64(2, _omitFieldNames ? '' : 'senderId')
|
||||
..aOS(3, _omitFieldNames ? '' : 'payload')
|
||||
..aOS(4, _omitFieldNames ? '' : 'info')
|
||||
..aOS(5, _omitFieldNames ? '' : 'href')
|
||||
..aInt64(6, _omitFieldNames ? '' : 'receivedAt')
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
WebxdcSyncUpdate clone() => deepCopy();
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
WebxdcSyncUpdate copyWith(void Function(WebxdcSyncUpdate) updates) =>
|
||||
super.copyWith((message) => updates(message as WebxdcSyncUpdate))
|
||||
as WebxdcSyncUpdate;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static WebxdcSyncUpdate create() => WebxdcSyncUpdate._();
|
||||
@$core.override
|
||||
WebxdcSyncUpdate createEmptyInstance() => create();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static WebxdcSyncUpdate getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<WebxdcSyncUpdate>(create);
|
||||
static WebxdcSyncUpdate? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get messageId => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set messageId($core.String value) => $_setString(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasMessageId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearMessageId() => $_clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$fixnum.Int64 get senderId => $_getI64(1);
|
||||
@$pb.TagNumber(2)
|
||||
set senderId($fixnum.Int64 value) => $_setInt64(1, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasSenderId() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearSenderId() => $_clearField(2);
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.String get payload => $_getSZ(2);
|
||||
@$pb.TagNumber(3)
|
||||
set payload($core.String value) => $_setString(2, value);
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasPayload() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearPayload() => $_clearField(3);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.String get info => $_getSZ(3);
|
||||
@$pb.TagNumber(4)
|
||||
set info($core.String value) => $_setString(3, value);
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasInfo() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearInfo() => $_clearField(4);
|
||||
|
||||
@$pb.TagNumber(5)
|
||||
$core.String get href => $_getSZ(4);
|
||||
@$pb.TagNumber(5)
|
||||
set href($core.String value) => $_setString(4, value);
|
||||
@$pb.TagNumber(5)
|
||||
$core.bool hasHref() => $_has(4);
|
||||
@$pb.TagNumber(5)
|
||||
void clearHref() => $_clearField(5);
|
||||
|
||||
@$pb.TagNumber(6)
|
||||
$fixnum.Int64 get receivedAt => $_getI64(5);
|
||||
@$pb.TagNumber(6)
|
||||
set receivedAt($fixnum.Int64 value) => $_setInt64(5, value);
|
||||
@$pb.TagNumber(6)
|
||||
$core.bool hasReceivedAt() => $_has(5);
|
||||
@$pb.TagNumber(6)
|
||||
void clearReceivedAt() => $_clearField(6);
|
||||
}
|
||||
|
||||
/// Attached to a message a webxdc app asked the user to send, so the chat can
|
||||
|
|
@ -402,6 +868,7 @@ class WebxdcUpdate extends $pb.GeneratedMessage {
|
|||
$core.String? href,
|
||||
$core.String? summary,
|
||||
$core.String? document,
|
||||
$core.String? notify,
|
||||
}) {
|
||||
final result = create();
|
||||
if (instanceId != null) result.instanceId = instanceId;
|
||||
|
|
@ -410,6 +877,7 @@ class WebxdcUpdate extends $pb.GeneratedMessage {
|
|||
if (href != null) result.href = href;
|
||||
if (summary != null) result.summary = summary;
|
||||
if (document != null) result.document = document;
|
||||
if (notify != null) result.notify = notify;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -431,6 +899,7 @@ class WebxdcUpdate extends $pb.GeneratedMessage {
|
|||
..aOS(4, _omitFieldNames ? '' : 'href')
|
||||
..aOS(5, _omitFieldNames ? '' : 'summary')
|
||||
..aOS(6, _omitFieldNames ? '' : 'document')
|
||||
..aOS(7, _omitFieldNames ? '' : 'notify')
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
|
|
@ -507,6 +976,17 @@ class WebxdcUpdate extends $pb.GeneratedMessage {
|
|||
$core.bool hasDocument() => $_has(5);
|
||||
@$pb.TagNumber(6)
|
||||
void clearDocument() => $_clearField(6);
|
||||
|
||||
/// JSON map of app-scoped member addresses to notification text.
|
||||
/// Absent preserves legacy broadcast; an empty map notifies nobody.
|
||||
@$pb.TagNumber(7)
|
||||
$core.String get notify => $_getSZ(6);
|
||||
@$pb.TagNumber(7)
|
||||
set notify($core.String value) => $_setString(6, value);
|
||||
@$pb.TagNumber(7)
|
||||
$core.bool hasNotify() => $_has(6);
|
||||
@$pb.TagNumber(7)
|
||||
void clearNotify() => $_clearField(7);
|
||||
}
|
||||
|
||||
const $core.bool _omitFieldNames =
|
||||
|
|
|
|||
|
|
@ -30,6 +30,11 @@ class AdditionalMessageData_Type extends $pb.ProtobufEnum {
|
|||
AdditionalMessageData_Type._(5, _omitEnumNames ? '' : 'WEBXDC_UPDATE');
|
||||
static const AdditionalMessageData_Type WEBXDC_SENT =
|
||||
AdditionalMessageData_Type._(6, _omitEnumNames ? '' : 'WEBXDC_SENT');
|
||||
static const AdditionalMessageData_Type WEBXDC_SYNC =
|
||||
AdditionalMessageData_Type._(7, _omitEnumNames ? '' : 'WEBXDC_SYNC');
|
||||
static const AdditionalMessageData_Type WEBXDC_SYNC_REQUEST =
|
||||
AdditionalMessageData_Type._(
|
||||
8, _omitEnumNames ? '' : 'WEBXDC_SYNC_REQUEST');
|
||||
|
||||
static const $core.List<AdditionalMessageData_Type> values =
|
||||
<AdditionalMessageData_Type>[
|
||||
|
|
@ -40,10 +45,12 @@ class AdditionalMessageData_Type extends $pb.ProtobufEnum {
|
|||
WEBXDC_APP,
|
||||
WEBXDC_UPDATE,
|
||||
WEBXDC_SENT,
|
||||
WEBXDC_SYNC,
|
||||
WEBXDC_SYNC_REQUEST,
|
||||
];
|
||||
|
||||
static final $core.List<AdditionalMessageData_Type?> _byValue =
|
||||
$pb.ProtobufEnum.$_initByValueList(values, 6);
|
||||
$pb.ProtobufEnum.$_initByValueList(values, 8);
|
||||
static AdditionalMessageData_Type? valueOf($core.int value) =>
|
||||
value < 0 || value >= _byValue.length ? null : _byValue[value];
|
||||
|
||||
|
|
|
|||
|
|
@ -106,6 +106,26 @@ const AdditionalMessageData$json = {
|
|||
'10': 'webxdcOrigin',
|
||||
'17': true
|
||||
},
|
||||
{
|
||||
'1': 'webxdc_sync',
|
||||
'3': 9,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.WebxdcSyncChunk',
|
||||
'9': 6,
|
||||
'10': 'webxdcSync',
|
||||
'17': true
|
||||
},
|
||||
{
|
||||
'1': 'webxdc_sync_request',
|
||||
'3': 10,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.WebxdcSyncRequest',
|
||||
'9': 7,
|
||||
'10': 'webxdcSyncRequest',
|
||||
'17': true
|
||||
},
|
||||
],
|
||||
'4': [AdditionalMessageData_Type$json],
|
||||
'8': [
|
||||
|
|
@ -115,6 +135,8 @@ const AdditionalMessageData$json = {
|
|||
{'1': '_webxdc_app'},
|
||||
{'1': '_webxdc_update'},
|
||||
{'1': '_webxdc_origin'},
|
||||
{'1': '_webxdc_sync'},
|
||||
{'1': '_webxdc_sync_request'},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
@ -129,6 +151,8 @@ const AdditionalMessageData_Type$json = {
|
|||
{'1': 'WEBXDC_APP', '2': 4},
|
||||
{'1': 'WEBXDC_UPDATE', '2': 5},
|
||||
{'1': 'WEBXDC_SENT', '2': 6},
|
||||
{'1': 'WEBXDC_SYNC', '2': 7},
|
||||
{'1': 'WEBXDC_SYNC_REQUEST', '2': 8},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
@ -141,11 +165,148 @@ final $typed_data.Uint8List additionalMessageDataDescriptor = $convert.base64Dec
|
|||
'BSABKANIAlIOYXNrQWJvdXRVc2VySWSIAQESLgoKd2VieGRjX2FwcBgGIAEoCzIKLldlYnhkY0'
|
||||
'FwcEgDUgl3ZWJ4ZGNBcHCIAQESNwoNd2VieGRjX3VwZGF0ZRgHIAEoCzINLldlYnhkY1VwZGF0'
|
||||
'ZUgEUgx3ZWJ4ZGNVcGRhdGWIAQESNwoNd2VieGRjX29yaWdpbhgIIAEoCzINLldlYnhkY09yaW'
|
||||
'dpbkgFUgx3ZWJ4ZGNPcmlnaW6IAQEiggEKBFR5cGUSCAoETElOSxAAEgwKCENPTlRBQ1RTEAES'
|
||||
'GgoWUkVTVE9SRURfRkxBTUVfQ09VTlRFUhACEhIKDkFTS19BQk9VVF9VU0VSEAMSDgoKV0VCWE'
|
||||
'RDX0FQUBAEEhEKDVdFQlhEQ19VUERBVEUQBRIPCgtXRUJYRENfU0VOVBAGQgcKBV9saW5rQhkK'
|
||||
'F19yZXN0b3JlZF9mbGFtZV9jb3VudGVyQhQKEl9hc2tfYWJvdXRfdXNlcl9pZEINCgtfd2VieG'
|
||||
'RjX2FwcEIQCg5fd2VieGRjX3VwZGF0ZUIQCg5fd2VieGRjX29yaWdpbg==');
|
||||
'dpbkgFUgx3ZWJ4ZGNPcmlnaW6IAQESNgoLd2VieGRjX3N5bmMYCSABKAsyEC5XZWJ4ZGNTeW5j'
|
||||
'Q2h1bmtIBlIKd2VieGRjU3luY4gBARJHChN3ZWJ4ZGNfc3luY19yZXF1ZXN0GAogASgLMhIuV2'
|
||||
'VieGRjU3luY1JlcXVlc3RIB1IRd2VieGRjU3luY1JlcXVlc3SIAQEirAEKBFR5cGUSCAoETElO'
|
||||
'SxAAEgwKCENPTlRBQ1RTEAESGgoWUkVTVE9SRURfRkxBTUVfQ09VTlRFUhACEhIKDkFTS19BQk'
|
||||
'9VVF9VU0VSEAMSDgoKV0VCWERDX0FQUBAEEhEKDVdFQlhEQ19VUERBVEUQBRIPCgtXRUJYRENf'
|
||||
'U0VOVBAGEg8KC1dFQlhEQ19TWU5DEAcSFwoTV0VCWERDX1NZTkNfUkVRVUVTVBAIQgcKBV9saW'
|
||||
'5rQhkKF19yZXN0b3JlZF9mbGFtZV9jb3VudGVyQhQKEl9hc2tfYWJvdXRfdXNlcl9pZEINCgtf'
|
||||
'd2VieGRjX2FwcEIQCg5fd2VieGRjX3VwZGF0ZUIQCg5fd2VieGRjX29yaWdpbkIOCgxfd2VieG'
|
||||
'RjX3N5bmNCFgoUX3dlYnhkY19zeW5jX3JlcXVlc3Q=');
|
||||
|
||||
@$core.Deprecated('Use webxdcSyncChunkDescriptor instead')
|
||||
const WebxdcSyncChunk$json = {
|
||||
'1': 'WebxdcSyncChunk',
|
||||
'2': [
|
||||
{'1': 'transfer_id', '3': 1, '4': 1, '5': 9, '10': 'transferId'},
|
||||
{'1': 'chunk_index', '3': 2, '4': 1, '5': 13, '10': 'chunkIndex'},
|
||||
{'1': 'chunk_count', '3': 3, '4': 1, '5': 13, '10': 'chunkCount'},
|
||||
{'1': 'payload_sha256', '3': 4, '4': 1, '5': 12, '10': 'payloadSha256'},
|
||||
{'1': 'payload', '3': 5, '4': 1, '5': 12, '10': 'payload'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `WebxdcSyncChunk`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List webxdcSyncChunkDescriptor = $convert.base64Decode(
|
||||
'Cg9XZWJ4ZGNTeW5jQ2h1bmsSHwoLdHJhbnNmZXJfaWQYASABKAlSCnRyYW5zZmVySWQSHwoLY2'
|
||||
'h1bmtfaW5kZXgYAiABKA1SCmNodW5rSW5kZXgSHwoLY2h1bmtfY291bnQYAyABKA1SCmNodW5r'
|
||||
'Q291bnQSJQoOcGF5bG9hZF9zaGEyNTYYBCABKAxSDXBheWxvYWRTaGEyNTYSGAoHcGF5bG9hZB'
|
||||
'gFIAEoDFIHcGF5bG9hZA==');
|
||||
|
||||
@$core.Deprecated('Use webxdcSyncRequestDescriptor instead')
|
||||
const WebxdcSyncRequest$json = {
|
||||
'1': 'WebxdcSyncRequest',
|
||||
};
|
||||
|
||||
/// Descriptor for `WebxdcSyncRequest`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List webxdcSyncRequestDescriptor =
|
||||
$convert.base64Decode('ChFXZWJ4ZGNTeW5jUmVxdWVzdA==');
|
||||
|
||||
@$core.Deprecated('Use webxdcSyncPayloadDescriptor instead')
|
||||
const WebxdcSyncPayload$json = {
|
||||
'1': 'WebxdcSyncPayload',
|
||||
'2': [
|
||||
{
|
||||
'1': 'instances',
|
||||
'3': 1,
|
||||
'4': 3,
|
||||
'5': 11,
|
||||
'6': '.WebxdcSyncInstance',
|
||||
'10': 'instances'
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `WebxdcSyncPayload`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List webxdcSyncPayloadDescriptor = $convert.base64Decode(
|
||||
'ChFXZWJ4ZGNTeW5jUGF5bG9hZBIxCglpbnN0YW5jZXMYASADKAsyEy5XZWJ4ZGNTeW5jSW5zdG'
|
||||
'FuY2VSCWluc3RhbmNlcw==');
|
||||
|
||||
@$core.Deprecated('Use webxdcSyncInstanceDescriptor instead')
|
||||
const WebxdcSyncInstance$json = {
|
||||
'1': 'WebxdcSyncInstance',
|
||||
'2': [
|
||||
{'1': 'instance_id', '3': 1, '4': 1, '5': 9, '10': 'instanceId'},
|
||||
{'1': 'app_id', '3': 2, '4': 1, '5': 9, '10': 'appId'},
|
||||
{'1': 'version', '3': 3, '4': 1, '5': 3, '10': 'version'},
|
||||
{
|
||||
'1': 'summary',
|
||||
'3': 4,
|
||||
'4': 1,
|
||||
'5': 9,
|
||||
'9': 0,
|
||||
'10': 'summary',
|
||||
'17': true
|
||||
},
|
||||
{
|
||||
'1': 'document',
|
||||
'3': 5,
|
||||
'4': 1,
|
||||
'5': 9,
|
||||
'9': 1,
|
||||
'10': 'document',
|
||||
'17': true
|
||||
},
|
||||
{'1': 'created_at', '3': 6, '4': 1, '5': 3, '10': 'createdAt'},
|
||||
{'1': 'last_update_at', '3': 7, '4': 1, '5': 3, '10': 'lastUpdateAt'},
|
||||
{
|
||||
'1': 'updates',
|
||||
'3': 8,
|
||||
'4': 3,
|
||||
'5': 11,
|
||||
'6': '.WebxdcSyncUpdate',
|
||||
'10': 'updates'
|
||||
},
|
||||
],
|
||||
'8': [
|
||||
{'1': '_summary'},
|
||||
{'1': '_document'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `WebxdcSyncInstance`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List webxdcSyncInstanceDescriptor = $convert.base64Decode(
|
||||
'ChJXZWJ4ZGNTeW5jSW5zdGFuY2USHwoLaW5zdGFuY2VfaWQYASABKAlSCmluc3RhbmNlSWQSFQ'
|
||||
'oGYXBwX2lkGAIgASgJUgVhcHBJZBIYCgd2ZXJzaW9uGAMgASgDUgd2ZXJzaW9uEh0KB3N1bW1h'
|
||||
'cnkYBCABKAlIAFIHc3VtbWFyeYgBARIfCghkb2N1bWVudBgFIAEoCUgBUghkb2N1bWVudIgBAR'
|
||||
'IdCgpjcmVhdGVkX2F0GAYgASgDUgljcmVhdGVkQXQSJAoObGFzdF91cGRhdGVfYXQYByABKANS'
|
||||
'DGxhc3RVcGRhdGVBdBIrCgd1cGRhdGVzGAggAygLMhEuV2VieGRjU3luY1VwZGF0ZVIHdXBkYX'
|
||||
'Rlc0IKCghfc3VtbWFyeUILCglfZG9jdW1lbnQ=');
|
||||
|
||||
@$core.Deprecated('Use webxdcSyncUpdateDescriptor instead')
|
||||
const WebxdcSyncUpdate$json = {
|
||||
'1': 'WebxdcSyncUpdate',
|
||||
'2': [
|
||||
{'1': 'message_id', '3': 1, '4': 1, '5': 9, '10': 'messageId'},
|
||||
{
|
||||
'1': 'sender_id',
|
||||
'3': 2,
|
||||
'4': 1,
|
||||
'5': 3,
|
||||
'9': 0,
|
||||
'10': 'senderId',
|
||||
'17': true
|
||||
},
|
||||
{'1': 'payload', '3': 3, '4': 1, '5': 9, '10': 'payload'},
|
||||
{'1': 'info', '3': 4, '4': 1, '5': 9, '9': 1, '10': 'info', '17': true},
|
||||
{'1': 'href', '3': 5, '4': 1, '5': 9, '9': 2, '10': 'href', '17': true},
|
||||
{'1': 'received_at', '3': 6, '4': 1, '5': 3, '10': 'receivedAt'},
|
||||
],
|
||||
'8': [
|
||||
{'1': '_sender_id'},
|
||||
{'1': '_info'},
|
||||
{'1': '_href'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `WebxdcSyncUpdate`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List webxdcSyncUpdateDescriptor = $convert.base64Decode(
|
||||
'ChBXZWJ4ZGNTeW5jVXBkYXRlEh0KCm1lc3NhZ2VfaWQYASABKAlSCW1lc3NhZ2VJZBIgCglzZW'
|
||||
'5kZXJfaWQYAiABKANIAFIIc2VuZGVySWSIAQESGAoHcGF5bG9hZBgDIAEoCVIHcGF5bG9hZBIX'
|
||||
'CgRpbmZvGAQgASgJSAFSBGluZm+IAQESFwoEaHJlZhgFIAEoCUgCUgRocmVmiAEBEh8KC3JlY2'
|
||||
'VpdmVkX2F0GAYgASgDUgpyZWNlaXZlZEF0QgwKCl9zZW5kZXJfaWRCBwoFX2luZm9CBwoFX2hy'
|
||||
'ZWY=');
|
||||
|
||||
@$core.Deprecated('Use webxdcOriginDescriptor instead')
|
||||
const WebxdcOrigin$json = {
|
||||
|
|
@ -202,12 +363,14 @@ const WebxdcUpdate$json = {
|
|||
'10': 'document',
|
||||
'17': true
|
||||
},
|
||||
{'1': 'notify', '3': 7, '4': 1, '5': 9, '9': 4, '10': 'notify', '17': true},
|
||||
],
|
||||
'8': [
|
||||
{'1': '_info'},
|
||||
{'1': '_href'},
|
||||
{'1': '_summary'},
|
||||
{'1': '_document'},
|
||||
{'1': '_notify'},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
@ -216,5 +379,5 @@ final $typed_data.Uint8List webxdcUpdateDescriptor = $convert.base64Decode(
|
|||
'CgxXZWJ4ZGNVcGRhdGUSHwoLaW5zdGFuY2VfaWQYASABKAlSCmluc3RhbmNlSWQSGAoHcGF5bG'
|
||||
'9hZBgCIAEoCVIHcGF5bG9hZBIXCgRpbmZvGAMgASgJSABSBGluZm+IAQESFwoEaHJlZhgEIAEo'
|
||||
'CUgBUgRocmVmiAEBEh0KB3N1bW1hcnkYBSABKAlIAlIHc3VtbWFyeYgBARIfCghkb2N1bWVudB'
|
||||
'gGIAEoCUgDUghkb2N1bWVudIgBAUIHCgVfaW5mb0IHCgVfaHJlZkIKCghfc3VtbWFyeUILCglf'
|
||||
'ZG9jdW1lbnQ=');
|
||||
'gGIAEoCUgDUghkb2N1bWVudIgBARIbCgZub3RpZnkYByABKAlIBFIGbm90aWZ5iAEBQgcKBV9p'
|
||||
'bmZvQgcKBV9ocmVmQgoKCF9zdW1tYXJ5QgsKCV9kb2N1bWVudEIJCgdfbm90aWZ5');
|
||||
|
|
|
|||
|
|
@ -46,16 +46,32 @@ class WebxdcService {
|
|||
/// cached with all its translations, so choosing one costs nothing and is
|
||||
/// never sent anywhere.
|
||||
static Future<List<rust_webxdc.WebxdcStoreApp>> catalog(
|
||||
String groupId,
|
||||
List<String> languages,
|
||||
) async {
|
||||
try {
|
||||
return await rust_webxdc.catalog(languages: languages);
|
||||
return await rust_webxdc.catalog(groupId: groupId, languages: languages);
|
||||
} catch (error) {
|
||||
Log.warn('reading the webxdc catalog failed: $error');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static Future<List<rust_webxdc.WebxdcOneTimeInstance>> oneTimeInstances(
|
||||
String groupId,
|
||||
List<String> languages,
|
||||
) async {
|
||||
try {
|
||||
return await rust_webxdc.oneTimeInstances(
|
||||
groupId: groupId,
|
||||
languages: languages,
|
||||
);
|
||||
} catch (error) {
|
||||
Log.warn('reading one-time webxdc apps for $groupId failed: $error');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// Places an app into a chat. Returns the id of the message carrying it,
|
||||
/// which is also the instance id.
|
||||
static Future<String?> createInstance(
|
||||
|
|
@ -77,10 +93,8 @@ class WebxdcService {
|
|||
|
||||
/// Gets the bundle an instance runs onto disk, verified.
|
||||
///
|
||||
/// Rust checks the store for a newer version first and moves the instance to
|
||||
/// it if there is one, so an app updates between two runs and never during
|
||||
/// one. Called when the user starts an app and never on message arrival: a
|
||||
/// message must not be able to make a device fetch anything.
|
||||
/// Opens cached code without a network update check. Only a bundle that
|
||||
/// has not been downloaded yet needs the server.
|
||||
static Future<String?> prepareBundle(String instanceId) async {
|
||||
try {
|
||||
return await rust_webxdc.prepareBundle(instanceId: instanceId);
|
||||
|
|
@ -90,6 +104,20 @@ class WebxdcService {
|
|||
}
|
||||
}
|
||||
|
||||
static final Set<String> _updatingInstances = {};
|
||||
|
||||
/// Best effort after closing. A failed check leaves the existing code usable.
|
||||
static Future<void> cacheUpdate(String instanceId) async {
|
||||
if (!_updatingInstances.add(instanceId)) return;
|
||||
try {
|
||||
await rust_webxdc.cacheUpdate(instanceId: instanceId);
|
||||
} catch (error) {
|
||||
Log.warn('checking for a webxdc update after closing failed: $error');
|
||||
} finally {
|
||||
_updatingInstances.remove(instanceId);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<rust_webxdc.WebxdcInstanceInfo?> instance(
|
||||
String instanceId,
|
||||
) async {
|
||||
|
|
@ -126,6 +154,7 @@ class WebxdcService {
|
|||
String? href,
|
||||
String? summary,
|
||||
String? document,
|
||||
String? notify,
|
||||
}) => rust_webxdc.sendUpdate(
|
||||
instanceId: instanceId,
|
||||
payload: payload,
|
||||
|
|
@ -133,6 +162,7 @@ class WebxdcService {
|
|||
href: href,
|
||||
summary: summary,
|
||||
document: document,
|
||||
notify: notify,
|
||||
);
|
||||
|
||||
/// Removes an instance, its whole update log, and the web storage its origin
|
||||
|
|
|
|||
|
|
@ -90,8 +90,7 @@ class WebxdcHost {
|
|||
/// Gets an app ready to run: makes sure the bundle is on disk and verified,
|
||||
/// and returns what the view needs to show it.
|
||||
///
|
||||
/// Rust checks the store for a newer version as part of this, so an app
|
||||
/// updates here and nowhere else. Returns a failure instead when the bundle
|
||||
/// Cached apps open without a network update check. Returns a failure when the bundle
|
||||
/// cannot be had at all -- a first start with no network, or an app that was
|
||||
/// taken out of the store before this device ever downloaded it.
|
||||
/// `languages` is what the reader prefers, most preferred first: the title
|
||||
|
|
@ -104,8 +103,7 @@ class WebxdcHost {
|
|||
if (instance == null) {
|
||||
return const WebxdcLaunch.failed('unavailable');
|
||||
}
|
||||
// The only place a bundle is ever fetched. A message arriving in a chat
|
||||
// never reaches this.
|
||||
// A first launch may need a download. Existing bundles are read locally.
|
||||
final bundlePath = await WebxdcService.prepareBundle(instanceId);
|
||||
if (bundlePath == null) {
|
||||
return const WebxdcLaunch.failed('unavailable');
|
||||
|
|
@ -148,6 +146,8 @@ class WebxdcHost {
|
|||
_updateSubscription = null;
|
||||
_openInstanceId = null;
|
||||
_closeView = null;
|
||||
// Closing never waits for the server; downloaded code is used next time.
|
||||
unawaited(WebxdcService.cacheUpdate(instanceId));
|
||||
}
|
||||
|
||||
static Future<void> _clearOrigin(String origin) async {
|
||||
|
|
@ -222,6 +222,10 @@ class WebxdcHost {
|
|||
final params = (message['params'] as Map<String, dynamic>?) ?? {};
|
||||
try {
|
||||
switch (message['method']) {
|
||||
case 'getMembers':
|
||||
final members = await rust_webxdc.members(instanceId: instanceId);
|
||||
return jsonEncode({'id': id, 'result': jsonDecode(members)});
|
||||
|
||||
case 'sendUpdate':
|
||||
// `payload` is re-encoded rather than passed through, so what is
|
||||
// stored and sent is JSON this side produced.
|
||||
|
|
@ -232,6 +236,9 @@ class WebxdcHost {
|
|||
href: params['href'] as String?,
|
||||
summary: params['summary'] as String?,
|
||||
document: params['document'] as String?,
|
||||
notify: params['notify'] == null
|
||||
? null
|
||||
: jsonEncode(params['notify']),
|
||||
);
|
||||
return jsonEncode({'id': id, 'result': null});
|
||||
|
||||
|
|
|
|||
|
|
@ -45,11 +45,10 @@ class GroupContextMenu extends StatelessWidget {
|
|||
),
|
||||
ContextMenuItem(
|
||||
title: context.lang.contextMenuOpenChat,
|
||||
onTap: () =>
|
||||
navigator.context.push(
|
||||
Routes.chatsMessages(group.groupId),
|
||||
extra: group,
|
||||
),
|
||||
onTap: () => navigator.context.push(
|
||||
Routes.chatsMessages(group.groupId),
|
||||
extra: group,
|
||||
),
|
||||
icon: FontAwesomeIcons.comments,
|
||||
),
|
||||
if (!group.archived)
|
||||
|
|
@ -76,17 +75,6 @@ class GroupContextMenu extends StatelessWidget {
|
|||
);
|
||||
if (ok) {
|
||||
await twonlyDB.messagesDao.deleteMessagesByGroupId(group.groupId);
|
||||
if (group.isDirectChat) {
|
||||
await twonlyDB.groupsDao.deleteGroup(group.groupId);
|
||||
} else {
|
||||
await twonlyDB.groupsDao.deleteGroup(group.groupId);
|
||||
// await twonlyDB.groupsDao.updateGroup(
|
||||
// group.groupId,
|
||||
// const GroupsCompanion(
|
||||
// deletedContent: Value(true),
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
|
|||
37
lib/src/visual/elements/pro_badge.element.dart
Normal file
37
lib/src/visual/elements/pro_badge.element.dart
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
|
||||
class ProBadge extends StatelessWidget {
|
||||
const ProBadge({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.amber.shade700,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const FaIcon(
|
||||
FontAwesomeIcons.star,
|
||||
size: 10,
|
||||
color: Colors.white,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
context.lang.backupCloudProBadge,
|
||||
style: const TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,11 +2,17 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:twonly/core/bridge/webxdc.dart' as rust_webxdc;
|
||||
import 'package:twonly/locator.dart';
|
||||
import 'package:twonly/src/constants/routes.keys.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/providers/purchases.provider.dart';
|
||||
import 'package:twonly/src/services/subscription.service.dart';
|
||||
import 'package:twonly/src/services/webxdc/webxdc.service.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/visual/elements/pro_badge.element.dart';
|
||||
|
||||
/// The in-app store.
|
||||
///
|
||||
|
|
@ -44,11 +50,13 @@ class _WebxdcStoreViewState extends State<WebxdcStoreView> {
|
|||
// The cached listing is shown first so the sheet is never empty while the
|
||||
// refresh is in flight.
|
||||
final languages = [language];
|
||||
final cached = await WebxdcService.catalog(languages);
|
||||
final cached = await WebxdcService.catalog(widget.group.groupId, languages);
|
||||
if (mounted) setState(() => _apps = cached);
|
||||
|
||||
final refreshed = await WebxdcService.refreshCatalog();
|
||||
final apps = refreshed ? await WebxdcService.catalog(languages) : cached;
|
||||
final apps = refreshed
|
||||
? await WebxdcService.catalog(widget.group.groupId, languages)
|
||||
: cached;
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_apps = apps;
|
||||
|
|
@ -57,6 +65,10 @@ class _WebxdcStoreViewState extends State<WebxdcStoreView> {
|
|||
}
|
||||
|
||||
Future<void> _place(rust_webxdc.WebxdcStoreApp app) async {
|
||||
if (app.proOnly && !isPayingUser(context.read<PurchasesProvider>().plan)) {
|
||||
await context.push(Routes.settingsSubscription);
|
||||
return;
|
||||
}
|
||||
setState(() => _placing = app.appId);
|
||||
final instanceId = await WebxdcService.createInstance(
|
||||
widget.group.groupId,
|
||||
|
|
@ -140,44 +152,70 @@ class _WebxdcStoreViewState extends State<WebxdcStoreView> {
|
|||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
controller: controller,
|
||||
itemCount: apps.length,
|
||||
itemBuilder: (context, index) {
|
||||
final app = apps[index];
|
||||
final icon = app.icon;
|
||||
// What the app says about itself, in the reader's language, as the
|
||||
// store published it. Plain text, bounded and stripped of control
|
||||
// characters before it was ever accepted for publishing.
|
||||
final subtitle = userService.currentUser.isDeveloper
|
||||
? ['v${app.version}', ?app.description].join(' · ')
|
||||
: app.description;
|
||||
final available = apps
|
||||
.where((app) => !app.oneTime || app.instanceId == null)
|
||||
.toList();
|
||||
final alreadyAdded = apps
|
||||
.where((app) => app.oneTime && app.instanceId != null)
|
||||
.toList();
|
||||
|
||||
return ListTile(
|
||||
leading: SizedBox(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: icon == null
|
||||
? const FaIcon(FontAwesomeIcons.puzzlePiece)
|
||||
: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Image.memory(icon, fit: BoxFit.cover),
|
||||
),
|
||||
return ListView(
|
||||
controller: controller,
|
||||
children: [
|
||||
...available.map(_appTile),
|
||||
if (alreadyAdded.isNotEmpty) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 18, 20, 6),
|
||||
child: Text(
|
||||
context.lang.webxdcStoreAlreadyAdded,
|
||||
style: Theme.of(context).textTheme.labelMedium?.copyWith(
|
||||
color: context.color.onSurfaceVariant,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
title: Text(app.name),
|
||||
subtitle: subtitle == null
|
||||
? null
|
||||
: Text(subtitle, maxLines: 2, overflow: TextOverflow.ellipsis),
|
||||
trailing: _placing == app.appId
|
||||
? const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: null,
|
||||
onTap: _placing == null ? () => _place(app) : null,
|
||||
);
|
||||
},
|
||||
...alreadyAdded.map(
|
||||
(app) =>
|
||||
Opacity(opacity: 0.45, child: _appTile(app, disabled: true)),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _appTile(
|
||||
rust_webxdc.WebxdcStoreApp app, {
|
||||
bool disabled = false,
|
||||
}) {
|
||||
final icon = app.icon;
|
||||
final subtitle = userService.currentUser.isDeveloper
|
||||
? ['v${app.version}', ?app.description].join(' · ')
|
||||
: app.description;
|
||||
return ListTile(
|
||||
leading: SizedBox(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: icon == null
|
||||
? const FaIcon(FontAwesomeIcons.puzzlePiece)
|
||||
: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Image.memory(icon, fit: BoxFit.cover),
|
||||
),
|
||||
),
|
||||
title: Text(app.name),
|
||||
subtitle: subtitle == null
|
||||
? null
|
||||
: Text(subtitle, maxLines: 2, overflow: TextOverflow.ellipsis),
|
||||
trailing: _placing == app.appId
|
||||
? const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: app.proOnly
|
||||
? const ProBadge()
|
||||
: null,
|
||||
onTap: !disabled && _placing == null ? () => _place(app) : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import 'package:twonly/src/visual/views/contact/contact_components/user_discover
|
|||
import 'package:twonly/src/visual/views/contact/contact_components/verification_expansion_tile.comp.dart';
|
||||
import 'package:twonly/src/visual/views/contact/select_contact_groups.view.dart';
|
||||
import 'package:twonly/src/visual/views/groups/group.view.dart';
|
||||
import 'package:twonly/src/visual/views/webxdc/webxdc_chat_apps.view.dart';
|
||||
|
||||
class ContactView extends StatefulWidget {
|
||||
const ContactView(this.userId, {super.key});
|
||||
|
|
@ -233,6 +234,18 @@ class _ContactViewState extends State<ContactView> {
|
|||
userService.currentUser.userId,
|
||||
),
|
||||
),
|
||||
BetterListTile(
|
||||
icon: FontAwesomeIcons.layerGroup,
|
||||
text: context.lang.webxdcChatAppsMenu,
|
||||
onTap: () => context.navPush(
|
||||
WebxdcChatAppsView(
|
||||
groupId: getUUIDforDirectChat(
|
||||
widget.userId,
|
||||
userService.currentUser.userId,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
ContactGroupsSubtitleBuilder(
|
||||
userId: contact.userId,
|
||||
builder: (context, subtitleWidget) {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import 'package:twonly/src/visual/views/contact/select_contact_groups.view.dart'
|
|||
import 'package:twonly/src/visual/views/groups/group_create_select_members.view.dart';
|
||||
import 'package:twonly/src/visual/views/groups/group_member.context.dart';
|
||||
import 'package:twonly/src/visual/views/settings/profile/profile.view.dart';
|
||||
import 'package:twonly/src/visual/views/webxdc/webxdc_chat_apps.view.dart';
|
||||
|
||||
class GroupView extends StatefulWidget {
|
||||
const GroupView(this.groupId, {super.key});
|
||||
|
|
@ -213,6 +214,13 @@ class _GroupViewState extends State<GroupView> {
|
|||
groupId: widget.groupId,
|
||||
disabled: !_group!.isGroupAdmin,
|
||||
),
|
||||
BetterListTile(
|
||||
icon: FontAwesomeIcons.layerGroup,
|
||||
text: context.lang.webxdcChatAppsMenu,
|
||||
onTap: () => context.navPush(
|
||||
WebxdcChatAppsView(groupId: widget.groupId),
|
||||
),
|
||||
),
|
||||
ContactGroupsSubtitleBuilder(
|
||||
groupId: widget.groupId,
|
||||
builder: (context, subtitleWidget) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:twonly/locator.dart';
|
||||
|
|
@ -12,6 +11,7 @@ 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/visual/elements/my_button.element.dart';
|
||||
import 'package:twonly/src/visual/elements/pro_badge.element.dart';
|
||||
|
||||
class MemoriesCloudBackupPromoComp extends StatelessWidget {
|
||||
const MemoriesCloudBackupPromoComp({super.key});
|
||||
|
|
@ -75,35 +75,7 @@ class MemoriesCloudBackupPromoComp extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.amber.shade700,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const FaIcon(
|
||||
FontAwesomeIcons.star,
|
||||
size: 10,
|
||||
color: Colors.white,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
context.lang.backupCloudProBadge,
|
||||
style: const TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const ProBadge(),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import 'package:twonly/src/services/user.service.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/pro_badge.element.dart';
|
||||
import 'package:twonly/src/visual/themes/light.dart';
|
||||
import 'package:twonly/src/visual/views/settings/backup/backup_utils.dart';
|
||||
import 'package:twonly/src/visual/views/settings/backup/components/recovery_card.comp.dart';
|
||||
|
|
@ -206,39 +207,12 @@ class _BackupViewState extends State<BackupView> {
|
|||
: '-')),
|
||||
),
|
||||
trailing: isFreePlan
|
||||
? Row(
|
||||
? const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.amber.shade700,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const FaIcon(
|
||||
FontAwesomeIcons.star,
|
||||
size: 10,
|
||||
color: Colors.white,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
context.lang.backupCloudProBadge,
|
||||
style: const TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Switch(
|
||||
ProBadge(),
|
||||
SizedBox(width: 8),
|
||||
Switch(
|
||||
value: false,
|
||||
onChanged: null,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ class _PlanCardState extends State<PlanCard> {
|
|||
context.lang.proFeature2,
|
||||
context.lang.proFeature3,
|
||||
context.lang.proFeature4,
|
||||
// context.lang.proFeature4,
|
||||
context.lang.subscriptionAllApps,
|
||||
];
|
||||
case 'Family':
|
||||
features = [
|
||||
|
|
@ -384,6 +384,7 @@ class _PlanCardState extends State<PlanCard> {
|
|||
context.lang.familyFeature2,
|
||||
context.lang.familyFeature3,
|
||||
context.lang.familyFeature4,
|
||||
context.lang.subscriptionAllApps,
|
||||
];
|
||||
default:
|
||||
}
|
||||
|
|
|
|||
112
lib/src/visual/views/webxdc/webxdc_chat_apps.view.dart
Normal file
112
lib/src/visual/views/webxdc/webxdc_chat_apps.view.dart
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/core/bridge/webxdc.dart' as rust_webxdc;
|
||||
import 'package:twonly/src/services/webxdc/webxdc.service.dart';
|
||||
import 'package:twonly/src/services/webxdc/webxdc_host.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/common.dart';
|
||||
import 'package:twonly/src/visual/views/webxdc/webxdc_app.view.dart';
|
||||
|
||||
/// One-time apps already attached to a chat, kept close to its profile.
|
||||
class WebxdcChatAppsView extends StatefulWidget {
|
||||
const WebxdcChatAppsView({required this.groupId, super.key});
|
||||
|
||||
final String groupId;
|
||||
|
||||
@override
|
||||
State<WebxdcChatAppsView> createState() => _WebxdcChatAppsViewState();
|
||||
}
|
||||
|
||||
class _WebxdcChatAppsViewState extends State<WebxdcChatAppsView> {
|
||||
List<rust_webxdc.WebxdcOneTimeInstance>? _apps;
|
||||
bool _loading = false;
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
if (_loading) return;
|
||||
_loading = true;
|
||||
_load();
|
||||
}
|
||||
|
||||
Future<void> _load() async {
|
||||
final apps = await WebxdcService.oneTimeInstances(
|
||||
widget.groupId,
|
||||
readerLanguages(context),
|
||||
);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_apps = apps;
|
||||
_loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _open(rust_webxdc.WebxdcOneTimeInstance app) async {
|
||||
final messenger = ScaffoldMessenger.of(context);
|
||||
final unavailable = context.lang.webxdcUnavailable;
|
||||
final launch = await WebxdcHost.prepare(
|
||||
app.instanceId,
|
||||
readerLanguages(context),
|
||||
);
|
||||
if (launch.failure != null) {
|
||||
messenger.showSnackBar(SnackBar(content: Text(unavailable)));
|
||||
return;
|
||||
}
|
||||
if (!mounted) return;
|
||||
await context.navPush(WebxdcAppView(launch: launch));
|
||||
await _load();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final apps = _apps;
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(context.lang.webxdcChatAppsTitle)),
|
||||
body: apps == null
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: apps.isEmpty
|
||||
? Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Text(
|
||||
context.lang.webxdcChatAppsEmpty,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
itemCount: apps.length,
|
||||
itemBuilder: (context, index) {
|
||||
final app = apps[index];
|
||||
final subtitle = app.document ?? app.summary;
|
||||
return ListTile(
|
||||
leading: SizedBox(
|
||||
width: 44,
|
||||
height: 44,
|
||||
child: app.icon == null
|
||||
? const Center(
|
||||
child: FaIcon(FontAwesomeIcons.puzzlePiece),
|
||||
)
|
||||
: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(9),
|
||||
child: Image.memory(app.icon!, fit: BoxFit.cover),
|
||||
),
|
||||
),
|
||||
title: Text(app.name),
|
||||
subtitle: subtitle == null
|
||||
? null
|
||||
: Text(
|
||||
subtitle,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () => _open(app),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT app_id AS \"app_id!: String\", version AS \"version!: i64\",\n name AS \"name!: String\",\n name_translations AS \"name_translations!: String\",\n source_code_url AS \"source_code_url: String\",\n description AS \"description!: String\",\n icon AS \"icon: Vec<u8>\", bundle_bytes AS \"bundle_bytes!: i64\"\n FROM webxdc_apps\n WHERE published = 1\n AND version = (SELECT MAX(version) FROM webxdc_apps AS newer\n WHERE newer.app_id = webxdc_apps.app_id AND newer.published = 1)\n ORDER BY name ASC",
|
||||
"query": "SELECT app_id AS \"app_id!: String\", version AS \"version!: i64\",\n name AS \"name!: String\",\n pro_only AS \"pro_only!: bool\",\n one_time AS \"one_time!: bool\",\n (SELECT instance_id FROM webxdc_instances\n WHERE webxdc_instances.group_id = ?\n AND webxdc_instances.app_id = webxdc_apps.app_id\n ORDER BY created_at ASC LIMIT 1) AS \"instance_id: String\",\n name_translations AS \"name_translations!: String\",\n source_code_url AS \"source_code_url: String\",\n description AS \"description!: String\",\n icon AS \"icon: Vec<u8>\", bundle_bytes AS \"bundle_bytes!: i64\"\n FROM webxdc_apps\n WHERE published = 1\n AND version = (SELECT MAX(version) FROM webxdc_apps AS newer\n WHERE newer.app_id = webxdc_apps.app_id AND newer.published = 1)\n ORDER BY sort_order ASC, name ASC, app_id ASC",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
|
@ -37,8 +37,41 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"name": "name_translations!: String",
|
||||
"name": "pro_only!: bool",
|
||||
"ordinal": 3,
|
||||
"type_info": "Bool",
|
||||
"origin": {
|
||||
"Table": {
|
||||
"table": "webxdc_apps",
|
||||
"name": "pro_only"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "one_time!: bool",
|
||||
"ordinal": 4,
|
||||
"type_info": "Bool",
|
||||
"origin": {
|
||||
"Table": {
|
||||
"table": "webxdc_apps",
|
||||
"name": "one_time"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "instance_id: String",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text",
|
||||
"origin": {
|
||||
"Table": {
|
||||
"table": "webxdc_instances",
|
||||
"name": "instance_id"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "name_translations!: String",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text",
|
||||
"origin": {
|
||||
"Table": {
|
||||
|
|
@ -49,7 +82,7 @@
|
|||
},
|
||||
{
|
||||
"name": "source_code_url: String",
|
||||
"ordinal": 4,
|
||||
"ordinal": 7,
|
||||
"type_info": "Text",
|
||||
"origin": {
|
||||
"Table": {
|
||||
|
|
@ -60,7 +93,7 @@
|
|||
},
|
||||
{
|
||||
"name": "description!: String",
|
||||
"ordinal": 5,
|
||||
"ordinal": 8,
|
||||
"type_info": "Text",
|
||||
"origin": {
|
||||
"Table": {
|
||||
|
|
@ -71,7 +104,7 @@
|
|||
},
|
||||
{
|
||||
"name": "icon: Vec<u8>",
|
||||
"ordinal": 6,
|
||||
"ordinal": 9,
|
||||
"type_info": "Blob",
|
||||
"origin": {
|
||||
"Table": {
|
||||
|
|
@ -82,7 +115,7 @@
|
|||
},
|
||||
{
|
||||
"name": "bundle_bytes!: i64",
|
||||
"ordinal": 7,
|
||||
"ordinal": 10,
|
||||
"type_info": "Integer",
|
||||
"origin": {
|
||||
"Table": {
|
||||
|
|
@ -93,18 +126,21 @@
|
|||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "b71a58e0499f8b69debddba47cf1ec86c4b99309441a4506e998beedfcead834"
|
||||
"hash": "1eb6b6e8e4ed43f43c3e6c570ffad96638f7c02d4baa4bdea006ea2a439ec24f"
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n UPDATE groups\n SET last_message_exchange = MAX(last_message_exchange, ?)\n WHERE group_id = ?\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "3d1fdb8baa14a261fd2e2943ce5712ed95f12e337e88c18619aed086ebbd42aa"
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n UPDATE groups\n SET last_message_exchange = MAX(last_message_exchange, ?),\n deleted_content = 0\n WHERE group_id = ?\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "3e9671207184c4df6b3d3c96bf4201160e557039a18115c4aa1737a80ebb5153"
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT instance.instance_id AS \"instance_id!: String\",\n app.name AS \"name!: String\",\n app.name_translations AS \"name_translations!: String\",\n app.icon AS \"icon: Vec<u8>\",\n instance.summary AS \"summary: String\",\n instance.document AS \"document: String\"\n FROM webxdc_instances AS instance\n JOIN webxdc_apps AS app\n ON app.app_id = instance.app_id AND app.version = instance.version\n WHERE instance.group_id = ? AND app.one_time = 1\n ORDER BY instance.last_update_at DESC, instance.created_at DESC",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "instance_id!: String",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text",
|
||||
"origin": {
|
||||
"Table": {
|
||||
"table": "webxdc_instances",
|
||||
"name": "instance_id"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "name!: String",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text",
|
||||
"origin": {
|
||||
"Table": {
|
||||
"table": "webxdc_apps",
|
||||
"name": "name"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "name_translations!: String",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text",
|
||||
"origin": {
|
||||
"Table": {
|
||||
"table": "webxdc_apps",
|
||||
"name": "name_translations"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "icon: Vec<u8>",
|
||||
"ordinal": 3,
|
||||
"type_info": "Blob",
|
||||
"origin": {
|
||||
"Table": {
|
||||
"table": "webxdc_apps",
|
||||
"name": "icon"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "summary: String",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text",
|
||||
"origin": {
|
||||
"Table": {
|
||||
"table": "webxdc_instances",
|
||||
"name": "summary"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "document: String",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text",
|
||||
"origin": {
|
||||
"Table": {
|
||||
"table": "webxdc_instances",
|
||||
"name": "document"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "7a89aa45a3186a9a4c712976e21d7a4e3c289b449bd5ce432845cbdc02b234a6"
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "INSERT INTO webxdc_apps\n (app_id, version, name, name_translations, source_code_url,\n description, icon, bundle_sha256, bundle_bytes, published,\n cached_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)\n ON CONFLICT(app_id, version) DO UPDATE SET\n name = excluded.name,\n name_translations = excluded.name_translations,\n source_code_url = excluded.source_code_url,\n description = excluded.description,\n icon = excluded.icon,\n bundle_sha256 = excluded.bundle_sha256,\n bundle_bytes = excluded.bundle_bytes,\n published = 1,\n cached_at = excluded.cached_at",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 10
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "cc6625aa285f9e6dcdc1a73d7bfb6ad23cff6c8932f61b48645c431dd7adaf48"
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "UPDATE groups SET last_message_exchange = CAST(strftime('%s','now') AS INTEGER), deleted_content = 0 WHERE group_id = ?",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "cdd3c7feb92de75c85642d5f99d0ac3f8bb715da50ba1fd25484041cd50a2818"
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "INSERT INTO webxdc_apps\n (app_id, version, name, name_translations, source_code_url,\n description, icon, bundle_sha256, bundle_bytes, published,\n cached_at, pro_only, sort_order, one_time)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?)\n ON CONFLICT(app_id, version) DO UPDATE SET\n name = excluded.name,\n name_translations = excluded.name_translations,\n source_code_url = excluded.source_code_url,\n description = excluded.description,\n icon = excluded.icon,\n bundle_sha256 = excluded.bundle_sha256,\n bundle_bytes = excluded.bundle_bytes,\n published = 1,\n pro_only = excluded.pro_only,\n sort_order = excluded.sort_order,\n one_time = excluded.one_time,\n cached_at = excluded.cached_at",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 13
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "fa52c200d8a2e33db876b41d77b460d0d697d3818061c59cf7c20106c1a64175"
|
||||
}
|
||||
|
|
@ -680,6 +680,7 @@ pub(crate) async fn handle_encrypted(
|
|||
let notification_content = content.clone();
|
||||
handle_encrypted_inner(ctx, t, from_user_id, receipt_id, content).await?;
|
||||
crate::services::notifications::record_incoming_event(
|
||||
ctx,
|
||||
t,
|
||||
from_user_id,
|
||||
receipt_id,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use prost::Message as ProstMessage;
|
|||
use sqlx::{Sqlite, Transaction};
|
||||
|
||||
pub(crate) async fn handle_additional_data_message(
|
||||
ctx: &Context,
|
||||
ctx: &std::sync::Arc<Context>,
|
||||
tr: &mut Transaction<'_, Sqlite>,
|
||||
from_user_id: i64,
|
||||
group_id: &str,
|
||||
|
|
@ -40,7 +40,20 @@ pub(crate) async fn handle_additional_data_message(
|
|||
};
|
||||
|
||||
if let Some(data) = data.as_ref() {
|
||||
if let Some(update) = data.webxdc_update.as_ref() {
|
||||
if data.webxdc_sync_request.is_some() {
|
||||
let ctx = ctx.clone();
|
||||
let group_id = group_id.to_owned();
|
||||
tokio::spawn(async move {
|
||||
if let Err(error) = WebxdcService::new(&ctx)
|
||||
.send_one_time_apps_to_contact(&group_id, from_user_id)
|
||||
.await
|
||||
{
|
||||
tracing::warn!(%error, "could not answer one-time app sync request");
|
||||
}
|
||||
});
|
||||
} else if let Some(sync) = data.webxdc_sync.as_ref() {
|
||||
WebxdcService::handle_sync_chunk(tr, group_id, from_user_id, sync).await?;
|
||||
} else if let Some(update) = data.webxdc_update.as_ref() {
|
||||
// App state belongs in the instance's own log, which is ordered,
|
||||
// gap free, and outside the reach of the chat's deletion timer.
|
||||
WebxdcService::handle_incoming_update(
|
||||
|
|
|
|||
|
|
@ -92,6 +92,11 @@ pub(crate) async fn handle_group_create(
|
|||
.await?;
|
||||
|
||||
GroupService::spawn_state_refresh_and_announce(ctx, group_id.to_owned());
|
||||
crate::services::webxdc::WebxdcService::spawn_sync_request(
|
||||
ctx,
|
||||
group_id.to_owned(),
|
||||
from_user_id,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,8 +133,13 @@ message WebxdcCatalogEntry {
|
|||
// The name in every language the bundle carries it in, keyed the same way.
|
||||
// `name` is what a client shows when none of them is a language it reads.
|
||||
map<string, string> name_translations = 10;
|
||||
bool pro_only = 11;
|
||||
// Only one instance of this app may be placed into a chat.
|
||||
bool one_time = 12;
|
||||
}
|
||||
|
||||
message WebxdcCatalog {
|
||||
// Admin-defined app order, with versions of each app grouped newest first.
|
||||
// Clients retain this order when caching and displaying the drawer.
|
||||
repeated WebxdcCatalogEntry entries = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ message AdditionalMessageData {
|
|||
WEBXDC_APP = 4;
|
||||
WEBXDC_UPDATE = 5;
|
||||
WEBXDC_SENT = 6;
|
||||
WEBXDC_SYNC = 7;
|
||||
WEBXDC_SYNC_REQUEST = 8;
|
||||
}
|
||||
Type type = 1;
|
||||
|
||||
|
|
@ -25,6 +27,45 @@ message AdditionalMessageData {
|
|||
optional WebxdcApp webxdc_app = 6;
|
||||
optional WebxdcUpdate webxdc_update = 7;
|
||||
optional WebxdcOrigin webxdc_origin = 8;
|
||||
optional WebxdcSyncChunk webxdc_sync = 9;
|
||||
optional WebxdcSyncRequest webxdc_sync_request = 10;
|
||||
}
|
||||
|
||||
// A peer-to-peer transfer of existing one-time app state. The encoded payload
|
||||
// is split into bounded hidden messages and installed only after every chunk
|
||||
// arrived and the digest matches.
|
||||
message WebxdcSyncChunk {
|
||||
string transfer_id = 1;
|
||||
uint32 chunk_index = 2;
|
||||
uint32 chunk_count = 3;
|
||||
bytes payload_sha256 = 4;
|
||||
bytes payload = 5;
|
||||
}
|
||||
|
||||
message WebxdcSyncRequest {}
|
||||
|
||||
message WebxdcSyncPayload {
|
||||
repeated WebxdcSyncInstance instances = 1;
|
||||
}
|
||||
|
||||
message WebxdcSyncInstance {
|
||||
string instance_id = 1;
|
||||
string app_id = 2;
|
||||
int64 version = 3;
|
||||
optional string summary = 4;
|
||||
optional string document = 5;
|
||||
int64 created_at = 6;
|
||||
int64 last_update_at = 7;
|
||||
repeated WebxdcSyncUpdate updates = 8;
|
||||
}
|
||||
|
||||
message WebxdcSyncUpdate {
|
||||
string message_id = 1;
|
||||
optional int64 sender_id = 2;
|
||||
string payload = 3;
|
||||
optional string info = 4;
|
||||
optional string href = 5;
|
||||
int64 received_at = 6;
|
||||
}
|
||||
|
||||
// Attached to a message a webxdc app asked the user to send, so the chat can
|
||||
|
|
@ -57,4 +98,7 @@ message WebxdcUpdate {
|
|||
optional string href = 4;
|
||||
optional string summary = 5;
|
||||
optional string document = 6;
|
||||
// JSON map of app-scoped member addresses to notification text.
|
||||
// Absent preserves legacy broadcast; an empty map notifies nobody.
|
||||
optional string notify = 7;
|
||||
}
|
||||
|
|
@ -126,9 +126,9 @@ impl Server {
|
|||
ResponseOk::PasswordlessNotificationMessages(msgs) => Some(msgs),
|
||||
// The server answers with `None` whenever the poll finds no unseen
|
||||
// message, which is the normal outcome once every share arrived.
|
||||
ResponseOk::None(_) => Some(
|
||||
proto::server_to_client::response::PasswordlessNotificationMessages::default(),
|
||||
),
|
||||
ResponseOk::None(_) => {
|
||||
Some(proto::server_to_client::response::PasswordlessNotificationMessages::default())
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,18 @@ pub struct WebxdcStoreApp {
|
|||
pub source_code_url: Option<String>,
|
||||
pub icon: Option<Vec<u8>>,
|
||||
pub bundle_bytes: i64,
|
||||
pub pro_only: bool,
|
||||
pub one_time: bool,
|
||||
/// The existing instance in this chat when a one-time app was already placed.
|
||||
pub instance_id: Option<String>,
|
||||
}
|
||||
|
||||
pub struct WebxdcOneTimeInstance {
|
||||
pub instance_id: String,
|
||||
pub name: String,
|
||||
pub icon: Option<Vec<u8>>,
|
||||
pub summary: Option<String>,
|
||||
pub document: Option<String>,
|
||||
}
|
||||
|
||||
pub struct WebxdcInstanceInfo {
|
||||
|
|
@ -66,12 +78,18 @@ pub async fn refresh_catalog() -> Result<()> {
|
|||
/// `languages` is what the UI prefers, most preferred first. Names and
|
||||
/// descriptions are cached in every language the catalog carries and picked
|
||||
/// here, so which language a user reads is never sent anywhere.
|
||||
pub async fn catalog(languages: Vec<String>) -> Result<Vec<WebxdcStoreApp>> {
|
||||
pub async fn catalog(group_id: String, languages: Vec<String>) -> Result<Vec<WebxdcStoreApp>> {
|
||||
let ctx = crate::context::Context::get_static()?;
|
||||
let database = ctx.app_db.read().await.clone();
|
||||
let rows = sqlx::query!(
|
||||
r#"SELECT app_id AS "app_id!: String", version AS "version!: i64",
|
||||
name AS "name!: String",
|
||||
pro_only AS "pro_only!: bool",
|
||||
one_time AS "one_time!: bool",
|
||||
(SELECT instance_id FROM webxdc_instances
|
||||
WHERE webxdc_instances.group_id = ?
|
||||
AND webxdc_instances.app_id = webxdc_apps.app_id
|
||||
ORDER BY created_at ASC LIMIT 1) AS "instance_id: String",
|
||||
name_translations AS "name_translations!: String",
|
||||
source_code_url AS "source_code_url: String",
|
||||
description AS "description!: String",
|
||||
|
|
@ -80,11 +98,12 @@ pub async fn catalog(languages: Vec<String>) -> Result<Vec<WebxdcStoreApp>> {
|
|||
WHERE published = 1
|
||||
AND version = (SELECT MAX(version) FROM webxdc_apps AS newer
|
||||
WHERE newer.app_id = webxdc_apps.app_id AND newer.published = 1)
|
||||
ORDER BY name ASC"#
|
||||
ORDER BY sort_order ASC, name ASC, app_id ASC"#,
|
||||
group_id,
|
||||
)
|
||||
.fetch_all(&database.pool)
|
||||
.await?;
|
||||
let mut apps: Vec<WebxdcStoreApp> = rows
|
||||
let apps: Vec<WebxdcStoreApp> = rows
|
||||
.into_iter()
|
||||
.map(|row| WebxdcStoreApp {
|
||||
app_id: row.app_id,
|
||||
|
|
@ -103,14 +122,54 @@ pub async fn catalog(languages: Vec<String>) -> Result<Vec<WebxdcStoreApp>> {
|
|||
source_code_url: row.source_code_url,
|
||||
icon: row.icon,
|
||||
bundle_bytes: row.bundle_bytes,
|
||||
pro_only: row.pro_only,
|
||||
one_time: row.one_time,
|
||||
instance_id: row.instance_id,
|
||||
})
|
||||
.collect();
|
||||
// The query orders by the untranslated name, which is not the order the
|
||||
// list is read in once the names are the reader's.
|
||||
apps.sort_by_key(|app| app.name.to_lowercase());
|
||||
// Keep the server order even when names are localized for the reader.
|
||||
Ok(apps)
|
||||
}
|
||||
|
||||
/// One-time apps already placed into a chat, for the profile shortcut.
|
||||
pub async fn one_time_instances(
|
||||
group_id: String,
|
||||
languages: Vec<String>,
|
||||
) -> Result<Vec<WebxdcOneTimeInstance>> {
|
||||
let ctx = crate::context::Context::get_static()?;
|
||||
let database = ctx.app_db.read().await.clone();
|
||||
let rows = sqlx::query!(
|
||||
r#"SELECT instance.instance_id AS "instance_id!: String",
|
||||
app.name AS "name!: String",
|
||||
app.name_translations AS "name_translations!: String",
|
||||
app.icon AS "icon: Vec<u8>",
|
||||
instance.summary AS "summary: String",
|
||||
instance.document AS "document: String"
|
||||
FROM webxdc_instances AS instance
|
||||
JOIN webxdc_apps AS app
|
||||
ON app.app_id = instance.app_id AND app.version = instance.version
|
||||
WHERE instance.group_id = ? AND app.one_time = 1
|
||||
ORDER BY instance.last_update_at DESC, instance.created_at DESC"#,
|
||||
group_id,
|
||||
)
|
||||
.fetch_all(&database.pool)
|
||||
.await?;
|
||||
Ok(rows
|
||||
.into_iter()
|
||||
.map(|row| WebxdcOneTimeInstance {
|
||||
instance_id: row.instance_id,
|
||||
name: crate::services::webxdc::store::pick_localized(
|
||||
&row.name_translations,
|
||||
&languages,
|
||||
)
|
||||
.unwrap_or(row.name),
|
||||
icon: row.icon,
|
||||
summary: row.summary,
|
||||
document: row.document,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Places an app into a chat and returns the id of the message that carries it.
|
||||
pub async fn create_instance(group_id: String, app_id: String, version: i64) -> Result<String> {
|
||||
let ctx = crate::context::Context::get_static()?;
|
||||
|
|
@ -135,8 +194,8 @@ pub async fn instance(instance_id: String) -> Result<Option<WebxdcInstanceInfo>>
|
|||
}))
|
||||
}
|
||||
|
||||
/// Downloads and verifies the bundle if it is not already on disk, and returns
|
||||
/// where it landed. Called when the user starts an app, never on message
|
||||
/// Opens cached code, adopting an update only if already downloaded.
|
||||
/// Downloads and verifies the pinned bundle only when it is missing. Called when the user starts an app, never on message
|
||||
/// arrival: a message must not be able to make a device fetch anything.
|
||||
pub async fn prepare_bundle(instance_id: String) -> Result<String> {
|
||||
let ctx = crate::context::Context::get_static()?;
|
||||
|
|
@ -144,6 +203,13 @@ pub async fn prepare_bundle(instance_id: String) -> Result<String> {
|
|||
Ok(path.display().to_string())
|
||||
}
|
||||
|
||||
/// Background update check after a webxdc app closes. Downloads only; the
|
||||
/// running version changes on a later prepare_bundle call, never mid-session.
|
||||
pub async fn cache_update(instance_id: String) -> Result<()> {
|
||||
let ctx = crate::context::Context::get_static()?;
|
||||
WebxdcService::new(ctx).cache_update(&instance_id).await
|
||||
}
|
||||
|
||||
/// Answers one request the webview made for a file inside the bundle.
|
||||
///
|
||||
/// The platform shim has already established that the request came from this
|
||||
|
|
@ -153,7 +219,7 @@ pub async fn serve(instance_id: String, request_path: String) -> Result<WebxdcRe
|
|||
let service = WebxdcService::new(ctx);
|
||||
// The bundle the instance is pinned to, not whatever the store offers now:
|
||||
// every file of one run comes out of the same `.xdc`, and the store was
|
||||
// already consulted when the app started.
|
||||
// never changed by a background update check.
|
||||
let path = service.pinned_bundle(&instance_id).await?;
|
||||
let init = service.init_script_values(&instance_id).await?;
|
||||
let response = bundle::serve(&path, &request_path, &init);
|
||||
|
|
@ -190,10 +256,11 @@ pub async fn send_update(
|
|||
href: Option<String>,
|
||||
summary: Option<String>,
|
||||
document: Option<String>,
|
||||
notify: Option<String>,
|
||||
) -> Result<()> {
|
||||
let ctx = crate::context::Context::get_static()?;
|
||||
WebxdcService::new(ctx)
|
||||
.send_update(instance_id, payload, info, href, summary, document)
|
||||
.send_update(instance_id, payload, info, href, summary, document, notify)
|
||||
.await
|
||||
}
|
||||
|
||||
|
|
@ -211,3 +278,9 @@ pub async fn delete_instance(instance_id: String) -> Result<Option<String>> {
|
|||
let ctx = crate::context::Context::get_static()?;
|
||||
WebxdcService::new(ctx).delete_instance(&instance_id).await
|
||||
}
|
||||
|
||||
/// Current members of the instance's chat; no account identifiers leave Rust.
|
||||
pub async fn members(instance_id: String) -> Result<String> {
|
||||
let ctx = crate::context::Context::get_static()?;
|
||||
WebxdcService::new(ctx).members(&instance_id).await
|
||||
}
|
||||
|
|
|
|||
1
rust/src/database/app/migrations/0019_webxdc_pro.sql
Normal file
1
rust/src/database/app/migrations/0019_webxdc_pro.sql
Normal file
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE webxdc_apps ADD COLUMN pro_only BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
2
rust/src/database/app/migrations/0020_webxdc_order.sql
Normal file
2
rust/src/database/app/migrations/0020_webxdc_order.sql
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
-- Retain the server catalog order when showing cached apps offline.
|
||||
ALTER TABLE webxdc_apps ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
-- Apps such as a shared expense ledger have one instance per chat.
|
||||
ALTER TABLE webxdc_apps ADD COLUMN one_time BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
34
rust/src/database/app/migrations/0022_webxdc_sync.sql
Normal file
34
rust/src/database/app/migrations/0022_webxdc_sync.sql
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
-- Chunks survive process restarts and out-of-order delivery. They are removed
|
||||
-- as soon as a complete, verified state transfer has been installed.
|
||||
CREATE TABLE webxdc_sync_chunks (
|
||||
transfer_id TEXT NOT NULL,
|
||||
group_id TEXT NOT NULL REFERENCES groups(group_id) ON DELETE CASCADE,
|
||||
sender_id INTEGER NOT NULL REFERENCES contacts(user_id) ON DELETE CASCADE,
|
||||
chunk_index INTEGER NOT NULL,
|
||||
chunk_count INTEGER NOT NULL,
|
||||
payload_sha256 BLOB NOT NULL,
|
||||
payload BLOB NOT NULL,
|
||||
created_at INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)),
|
||||
PRIMARY KEY (transfer_id, sender_id, chunk_index)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_webxdc_sync_chunks_created_at
|
||||
ON webxdc_sync_chunks(created_at);
|
||||
|
||||
-- A newly joined member can receive a live update before the instance snapshot.
|
||||
-- Keep it until the app card arrives instead of dropping shared state.
|
||||
CREATE TABLE webxdc_pending_updates (
|
||||
message_id TEXT NOT NULL PRIMARY KEY,
|
||||
instance_id TEXT NOT NULL,
|
||||
group_id TEXT NOT NULL REFERENCES groups(group_id) ON DELETE CASCADE,
|
||||
sender_id INTEGER NOT NULL REFERENCES contacts(user_id) ON DELETE CASCADE,
|
||||
payload TEXT NOT NULL,
|
||||
info TEXT,
|
||||
href TEXT,
|
||||
summary TEXT,
|
||||
document TEXT,
|
||||
received_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_webxdc_pending_updates_instance
|
||||
ON webxdc_pending_updates(instance_id, received_at);
|
||||
|
|
@ -330,7 +330,8 @@ impl Group {
|
|||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE groups
|
||||
SET last_message_exchange = MAX(last_message_exchange, ?)
|
||||
SET last_message_exchange = MAX(last_message_exchange, ?),
|
||||
deleted_content = 0
|
||||
WHERE group_id = ?
|
||||
"#,
|
||||
timestamp,
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ flutter_rust_bridge::frb_generated_boilerplate!(
|
|||
default_rust_auto_opaque = RustAutoOpaqueMoi,
|
||||
);
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.12.0";
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1362207365;
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1591152700;
|
||||
|
||||
// Section: executor
|
||||
|
||||
|
|
@ -161,6 +161,43 @@ fn wire__crate__bridge__webxdc__address_for_impl(
|
|||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__bridge__webxdc__cache_update_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
rust_vec_len_: i32,
|
||||
data_len_: i32,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::SseCodec, _, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "cache_update",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let message = unsafe {
|
||||
flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
|
||||
ptr_,
|
||||
rust_vec_len_,
|
||||
data_len_,
|
||||
)
|
||||
};
|
||||
let mut deserializer =
|
||||
flutter_rust_bridge::for_generated::SseDeserializer::new(message);
|
||||
let api_instance_id = <String>::sse_decode(&mut deserializer);
|
||||
deserializer.end();
|
||||
move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>(
|
||||
(move || async move {
|
||||
let output_ok =
|
||||
crate::bridge::webxdc::cache_update(api_instance_id).await?;
|
||||
Ok(output_ok)
|
||||
})()
|
||||
.await,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__bridge__webxdc__catalog_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
|
|
@ -183,12 +220,14 @@ fn wire__crate__bridge__webxdc__catalog_impl(
|
|||
};
|
||||
let mut deserializer =
|
||||
flutter_rust_bridge::for_generated::SseDeserializer::new(message);
|
||||
let api_group_id = <String>::sse_decode(&mut deserializer);
|
||||
let api_languages = <Vec<String>>::sse_decode(&mut deserializer);
|
||||
deserializer.end();
|
||||
move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>(
|
||||
(move || async move {
|
||||
let output_ok = crate::bridge::webxdc::catalog(api_languages).await?;
|
||||
let output_ok =
|
||||
crate::bridge::webxdc::catalog(api_group_id, api_languages).await?;
|
||||
Ok(output_ok)
|
||||
})()
|
||||
.await,
|
||||
|
|
@ -820,6 +859,81 @@ fn wire__crate__bridge__groups__manage_admin_state_impl(
|
|||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__bridge__webxdc__members_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
rust_vec_len_: i32,
|
||||
data_len_: i32,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::SseCodec, _, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "members",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let message = unsafe {
|
||||
flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
|
||||
ptr_,
|
||||
rust_vec_len_,
|
||||
data_len_,
|
||||
)
|
||||
};
|
||||
let mut deserializer =
|
||||
flutter_rust_bridge::for_generated::SseDeserializer::new(message);
|
||||
let api_instance_id = <String>::sse_decode(&mut deserializer);
|
||||
deserializer.end();
|
||||
move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>(
|
||||
(move || async move {
|
||||
let output_ok = crate::bridge::webxdc::members(api_instance_id).await?;
|
||||
Ok(output_ok)
|
||||
})()
|
||||
.await,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__bridge__webxdc__one_time_instances_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
rust_vec_len_: i32,
|
||||
data_len_: i32,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::SseCodec, _, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "one_time_instances",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let message = unsafe {
|
||||
flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
|
||||
ptr_,
|
||||
rust_vec_len_,
|
||||
data_len_,
|
||||
)
|
||||
};
|
||||
let mut deserializer =
|
||||
flutter_rust_bridge::for_generated::SseDeserializer::new(message);
|
||||
let api_group_id = <String>::sse_decode(&mut deserializer);
|
||||
let api_languages = <Vec<String>>::sse_decode(&mut deserializer);
|
||||
deserializer.end();
|
||||
move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>(
|
||||
(move || async move {
|
||||
let output_ok =
|
||||
crate::bridge::webxdc::one_time_instances(api_group_id, api_languages)
|
||||
.await?;
|
||||
Ok(output_ok)
|
||||
})()
|
||||
.await,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__bridge__webxdc__prepare_bundle_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
|
||||
|
|
@ -5592,6 +5706,7 @@ fn wire__crate__bridge__webxdc__send_update_impl(
|
|||
let api_href = <Option<String>>::sse_decode(&mut deserializer);
|
||||
let api_summary = <Option<String>>::sse_decode(&mut deserializer);
|
||||
let api_document = <Option<String>>::sse_decode(&mut deserializer);
|
||||
let api_notify = <Option<String>>::sse_decode(&mut deserializer);
|
||||
deserializer.end();
|
||||
move |context| async move {
|
||||
transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>(
|
||||
|
|
@ -5603,6 +5718,7 @@ fn wire__crate__bridge__webxdc__send_update_impl(
|
|||
api_href,
|
||||
api_summary,
|
||||
api_document,
|
||||
api_notify,
|
||||
)
|
||||
.await?;
|
||||
Ok(output_ok)
|
||||
|
|
@ -6671,6 +6787,20 @@ impl SseDecode for Vec<crate::database::app::SqlValue> {
|
|||
}
|
||||
}
|
||||
|
||||
impl SseDecode for Vec<crate::bridge::webxdc::WebxdcOneTimeInstance> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
let mut len_ = <i32>::sse_decode(deserializer);
|
||||
let mut ans_ = Vec::with_capacity(len_ as usize);
|
||||
for idx_ in 0..len_ {
|
||||
ans_.push(<crate::bridge::webxdc::WebxdcOneTimeInstance>::sse_decode(
|
||||
deserializer,
|
||||
));
|
||||
}
|
||||
return ans_;
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for Vec<crate::bridge::webxdc::WebxdcStoreApp> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
|
|
@ -7330,6 +7460,24 @@ impl SseDecode for crate::bridge::webxdc::WebxdcInstanceInfo {
|
|||
}
|
||||
}
|
||||
|
||||
impl SseDecode for crate::bridge::webxdc::WebxdcOneTimeInstance {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
let mut var_instanceId = <String>::sse_decode(deserializer);
|
||||
let mut var_name = <String>::sse_decode(deserializer);
|
||||
let mut var_icon = <Option<Vec<u8>>>::sse_decode(deserializer);
|
||||
let mut var_summary = <Option<String>>::sse_decode(deserializer);
|
||||
let mut var_document = <Option<String>>::sse_decode(deserializer);
|
||||
return crate::bridge::webxdc::WebxdcOneTimeInstance {
|
||||
instance_id: var_instanceId,
|
||||
name: var_name,
|
||||
icon: var_icon,
|
||||
summary: var_summary,
|
||||
document: var_document,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl SseDecode for crate::bridge::webxdc::WebxdcResponse {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||
|
|
@ -7358,6 +7506,9 @@ impl SseDecode for crate::bridge::webxdc::WebxdcStoreApp {
|
|||
let mut var_sourceCodeUrl = <Option<String>>::sse_decode(deserializer);
|
||||
let mut var_icon = <Option<Vec<u8>>>::sse_decode(deserializer);
|
||||
let mut var_bundleBytes = <i64>::sse_decode(deserializer);
|
||||
let mut var_proOnly = <bool>::sse_decode(deserializer);
|
||||
let mut var_oneTime = <bool>::sse_decode(deserializer);
|
||||
let mut var_instanceId = <Option<String>>::sse_decode(deserializer);
|
||||
return crate::bridge::webxdc::WebxdcStoreApp {
|
||||
app_id: var_appId,
|
||||
version: var_version,
|
||||
|
|
@ -7366,6 +7517,9 @@ impl SseDecode for crate::bridge::webxdc::WebxdcStoreApp {
|
|||
source_code_url: var_sourceCodeUrl,
|
||||
icon: var_icon,
|
||||
bundle_bytes: var_bundleBytes,
|
||||
pro_only: var_proOnly,
|
||||
one_time: var_oneTime,
|
||||
instance_id: var_instanceId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -7400,163 +7554,166 @@ fn pde_ffi_dispatcher_primary_impl(
|
|||
1 => wire__crate__bridge__groups__add_hidden_contact_impl(port, ptr, rust_vec_len, data_len),
|
||||
2 => wire__crate__bridge__groups__add_new_group_members_impl(port, ptr, rust_vec_len, data_len),
|
||||
3 => wire__crate__bridge__webxdc__address_for_impl(port, ptr, rust_vec_len, data_len),
|
||||
4 => wire__crate__bridge__webxdc__catalog_impl(port, ptr, rust_vec_len, data_len),
|
||||
5 => wire__crate__bridge__logging__clean_log_file_impl(port, ptr, rust_vec_len, data_len),
|
||||
6 => wire__crate__bridge__logging__clear_log_file_impl(port, ptr, rust_vec_len, data_len),
|
||||
7 => wire__crate__bridge__webxdc__create_instance_impl(port, ptr, rust_vec_len, data_len),
|
||||
8 => wire__crate__bridge__groups__create_new_group_impl(port, ptr, rust_vec_len, data_len),
|
||||
9 => wire__crate__bridge__webxdc__delete_instance_impl(port, ptr, rust_vec_len, data_len),
|
||||
10 => wire__crate__bridge__groups__fetch_group_state_impl(port, ptr, rust_vec_len, data_len),
|
||||
11 => wire__crate__bridge__groups__fetch_group_states_for_unjoined_groups_impl(port, ptr, rust_vec_len, data_len),
|
||||
12 => wire__crate__bridge__groups__fetch_missing_group_public_keys_impl(port, ptr, rust_vec_len, data_len),
|
||||
13 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_change_exclusion_for_contact_impl(port, ptr, rust_vec_len, data_len),
|
||||
14 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_get_current_version_impl(port, ptr, rust_vec_len, data_len),
|
||||
15 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_update_verification_state_for_user_impl(port, ptr, rust_vec_len, data_len),
|
||||
16 => wire__crate__bridge__callbacks__init_flutter_callbacks_impl(port, ptr, rust_vec_len, data_len),
|
||||
17 => wire__crate__bridge__initialize_twonly_flutter_impl(port, ptr, rust_vec_len, data_len),
|
||||
18 => wire__crate__bridge__initialize_twonly_standalone_impl(port, ptr, rust_vec_len, data_len),
|
||||
19 => wire__crate__bridge__webxdc__instance_impl(port, ptr, rust_vec_len, data_len),
|
||||
20 => wire__crate__bridge__groups__leave_group_impl(port, ptr, rust_vec_len, data_len),
|
||||
21 => wire__crate__bridge__logging__load_log_file_impl(port, ptr, rust_vec_len, data_len),
|
||||
22 => wire__crate__bridge__groups__manage_admin_state_impl(port, ptr, rust_vec_len, data_len),
|
||||
23 => wire__crate__bridge__webxdc__prepare_bundle_impl(port, ptr, rust_vec_len, data_len),
|
||||
24 => wire__crate__bridge__logging__read_last_log_lines_impl(port, ptr, rust_vec_len, data_len),
|
||||
25 => wire__crate__bridge__webxdc__refresh_catalog_impl(port, ptr, rust_vec_len, data_len),
|
||||
26 => wire__crate__bridge__groups__remove_member_from_group_impl(port, ptr, rust_vec_len, data_len),
|
||||
27 => wire__crate__bridge__api__rust_api_abandon_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
28 => wire__crate__bridge__api__rust_api_add_additional_user_impl(port, ptr, rust_vec_len, data_len),
|
||||
29 => wire__crate__bridge__api__rust_api_allocate_sequence_impl(port, ptr, rust_vec_len, data_len),
|
||||
31 => wire__crate__bridge__api__rust_api_authentication_headers_impl(port, ptr, rust_vec_len, data_len),
|
||||
33 => wire__crate__bridge__api__rust_api_change_username_impl(port, ptr, rust_vec_len, data_len),
|
||||
34 => wire__crate__bridge__api__rust_api_check_for_deleted_usernames_impl(port, ptr, rust_vec_len, data_len),
|
||||
35 => wire__crate__bridge__api__rust_api_check_for_passwordless_notification_impl(port, ptr, rust_vec_len, data_len),
|
||||
36 => wire__crate__bridge__api__rust_api_clear_contact_request_notifications_impl(port, ptr, rust_vec_len, data_len),
|
||||
37 => wire__crate__bridge__api__rust_api_clear_conversation_notifications_impl(port, ptr, rust_vec_len, data_len),
|
||||
38 => wire__crate__bridge__api__rust_api_close_impl(port, ptr, rust_vec_len, data_len),
|
||||
39 => wire__crate__bridge__api__rust_api_confirm_memories_upload_impl(port, ptr, rust_vec_len, data_len),
|
||||
40 => wire__crate__bridge__api__rust_api_connect_impl(port, ptr, rust_vec_len, data_len),
|
||||
41 => wire__crate__bridge__api__rust_api_connection_state_impl(port, ptr, rust_vec_len, data_len),
|
||||
42 => wire__crate__bridge__api__rust_api_crop_media_transparent_borders_impl(port, ptr, rust_vec_len, data_len),
|
||||
43 => wire__crate__bridge__api__rust_api_current_user_avatar_path_impl(port, ptr, rust_vec_len, data_len),
|
||||
45 => wire__crate__bridge__api__rust_api_delete_account_impl(port, ptr, rust_vec_len, data_len),
|
||||
46 => wire__crate__bridge__api__rust_api_delete_memory_impl(port, ptr, rust_vec_len, data_len),
|
||||
47 => wire__crate__bridge__api__rust_api_delete_widget_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
48 => wire__crate__bridge__api__rust_api_disable_memories_backup_impl(port, ptr, rust_vec_len, data_len),
|
||||
49 => wire__crate__bridge__api__rust_api_download_done_impl(port, ptr, rust_vec_len, data_len),
|
||||
50 => wire__crate__bridge__api__rust_api_download_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
51 => wire__crate__bridge__api__rust_api_download_pending_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
52 => wire__crate__bridge__api__rust_api_ensure_avatar_png_impl(port, ptr, rust_vec_len, data_len),
|
||||
53 => wire__crate__bridge__api__rust_api_establish_signal_session_impl(port, ptr, rust_vec_len, data_len),
|
||||
54 => wire__crate__bridge__api__rust_api_events_impl(port, ptr, rust_vec_len, data_len),
|
||||
55 => wire__crate__bridge__api__rust_api_finish_started_media_uploads_impl(port, ptr, rust_vec_len, data_len),
|
||||
56 => wire__crate__bridge__api__rust_api_force_ipa_check_impl(port, ptr, rust_vec_len, data_len),
|
||||
57 => wire__crate__bridge__api__rust_api_get_memories_url_impl(port, ptr, rust_vec_len, data_len),
|
||||
58 => wire__crate__bridge__api__rust_api_get_memories_usage_impl(port, ptr, rust_vec_len, data_len),
|
||||
59 => wire__crate__bridge__api__rust_api_get_plan_balance_impl(port, ptr, rust_vec_len, data_len),
|
||||
60 => wire__crate__bridge__api__rust_api_get_proof_of_work_impl(port, ptr, rust_vec_len, data_len),
|
||||
61 => wire__crate__bridge__api__rust_api_get_server_key_for_passwordless_recovery_impl(port, ptr, rust_vec_len, data_len),
|
||||
62 => wire__crate__bridge__api__rust_api_get_user_by_id_impl(port, ptr, rust_vec_len, data_len),
|
||||
63 => wire__crate__bridge__api__rust_api_get_user_data_impl(port, ptr, rust_vec_len, data_len),
|
||||
64 => wire__crate__bridge__api__rust_api_get_user_id_from_username_impl(port, ptr, rust_vec_len, data_len),
|
||||
65 => wire__crate__bridge__api__rust_api_hand_outbox_to_os_impl(port, ptr, rust_vec_len, data_len),
|
||||
66 => wire__crate__bridge__api__rust_api_initialize_media_upload_impl(port, ptr, rust_vec_len, data_len),
|
||||
67 => wire__crate__bridge__api__rust_api_insert_and_send_additional_data_impl(port, ptr, rust_vec_len, data_len),
|
||||
68 => wire__crate__bridge__api__rust_api_insert_and_send_ask_about_user_impl(port, ptr, rust_vec_len, data_len),
|
||||
69 => wire__crate__bridge__api__rust_api_insert_and_send_contact_share_impl(port, ptr, rust_vec_len, data_len),
|
||||
70 => wire__crate__bridge__api__rust_api_insert_and_send_text_impl(port, ptr, rust_vec_len, data_len),
|
||||
71 => wire__crate__bridge__api__rust_api_ipa_purchase_impl(port, ptr, rust_vec_len, data_len),
|
||||
72 => wire__crate__bridge__api__rust_api_load_plan_balance_impl(port, ptr, rust_vec_len, data_len),
|
||||
73 => wire__crate__bridge__api__rust_api_media_size_limit_report_impl(port, ptr, rust_vec_len, data_len),
|
||||
74 => wire__crate__bridge__api__rust_api_media_step_finished_impl(port, ptr, rust_vec_len, data_len),
|
||||
75 => wire__crate__bridge__api__rust_api_notification_badge_count_impl(port, ptr, rust_vec_len, data_len),
|
||||
76 => wire__crate__bridge__api__rust_api_notify_messages_opened_impl(port, ptr, rust_vec_len, data_len),
|
||||
77 => wire__crate__bridge__api__rust_api_perform_passwordless_recovery_heartbeat_impl(port, ptr, rust_vec_len, data_len),
|
||||
78 => wire__crate__bridge__api__rust_api_prerender_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
79 => wire__crate__bridge__api__rust_api_purge_media_temp_folder_impl(port, ptr, rust_vec_len, data_len),
|
||||
80 => wire__crate__bridge__api__rust_api_purge_widget_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
81 => wire__crate__bridge__api__rust_api_refresh_widget_manifest_impl(port, ptr, rust_vec_len, data_len),
|
||||
82 => wire__crate__bridge__api__rust_api_register_impl(port, ptr, rust_vec_len, data_len),
|
||||
83 => wire__crate__bridge__api__rust_api_register_home_widget_impl(port, ptr, rust_vec_len, data_len),
|
||||
84 => wire__crate__bridge__api__rust_api_register_passwordless_notification_impl(port, ptr, rust_vec_len, data_len),
|
||||
85 => wire__crate__bridge__api__rust_api_register_passwordless_recovery_impl(port, ptr, rust_vec_len, data_len),
|
||||
86 => wire__crate__bridge__api__rust_api_reload_configuration_impl(port, ptr, rust_vec_len, data_len),
|
||||
87 => wire__crate__bridge__api__rust_api_remove_additional_user_impl(port, ptr, rust_vec_len, data_len),
|
||||
88 => wire__crate__bridge__api__rust_api_remove_media_files_impl(port, ptr, rust_vec_len, data_len),
|
||||
89 => wire__crate__bridge__api__rust_api_report_user_impl(port, ptr, rust_vec_len, data_len),
|
||||
90 => wire__crate__bridge__api__rust_api_request_binary_impl(port, ptr, rust_vec_len, data_len),
|
||||
91 => wire__crate__bridge__api__rust_api_request_contact_by_username_impl(port, ptr, rust_vec_len, data_len),
|
||||
92 => wire__crate__bridge__api__rust_api_request_media_reupload_impl(port, ptr, rust_vec_len, data_len),
|
||||
93 => wire__crate__bridge__api__rust_api_request_memories_upload_impl(port, ptr, rust_vec_len, data_len),
|
||||
94 => wire__crate__bridge__api__rust_api_retransmit_all_messages_impl(port, ptr, rust_vec_len, data_len),
|
||||
95 => wire__crate__bridge__api__rust_api_retry_pending_media_reuploads_impl(port, ptr, rust_vec_len, data_len),
|
||||
96 => wire__crate__bridge__api__rust_api_reupload_pending_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
97 => wire__crate__bridge__api__rust_api_save_media_to_gallery_impl(port, ptr, rust_vec_len, data_len),
|
||||
98 => wire__crate__bridge__api__rust_api_send_binary_impl(port, ptr, rust_vec_len, data_len),
|
||||
99 => wire__crate__bridge__api__rust_api_send_contact_profile_impl(port, ptr, rust_vec_len, data_len),
|
||||
100 => wire__crate__bridge__api__rust_api_send_encrypted_content_impl(port, ptr, rust_vec_len, data_len),
|
||||
101 => wire__crate__bridge__api__rust_api_send_encrypted_content_to_group_impl(port, ptr, rust_vec_len, data_len),
|
||||
102 => wire__crate__bridge__api__rust_api_send_media_to_groups_impl(port, ptr, rust_vec_len, data_len),
|
||||
103 => wire__crate__bridge__api__rust_api_send_queued_message_impl(port, ptr, rust_vec_len, data_len),
|
||||
104 => wire__crate__bridge__api__rust_api_send_text_message_impl(port, ptr, rust_vec_len, data_len),
|
||||
105 => wire__crate__bridge__api__rust_api_send_typing_impl(port, ptr, rust_vec_len, data_len),
|
||||
106 => wire__crate__bridge__api__rust_api_set_background_impl(port, ptr, rust_vec_len, data_len),
|
||||
107 => wire__crate__bridge__api__rust_api_set_home_widget_groups_impl(port, ptr, rust_vec_len, data_len),
|
||||
108 => wire__crate__bridge__api__rust_api_set_login_token_impl(port, ptr, rust_vec_len, data_len),
|
||||
109 => wire__crate__bridge__api__rust_api_set_media_display_limit_impl(port, ptr, rust_vec_len, data_len),
|
||||
110 => wire__crate__bridge__api__rust_api_set_media_requires_authentication_impl(port, ptr, rust_vec_len, data_len),
|
||||
111 => wire__crate__bridge__api__rust_api_set_media_trim_impl(port, ptr, rust_vec_len, data_len),
|
||||
112 => wire__crate__bridge__api__rust_api_set_network_available_impl(port, ptr, rust_vec_len, data_len),
|
||||
113 => wire__crate__bridge__api__rust_api_store_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
114 => wire__crate__bridge__api__rust_api_submit_recovery_share_impl(port, ptr, rust_vec_len, data_len),
|
||||
115 => wire__crate__bridge__api__rust_api_sync_widget_permissions_impl(port, ptr, rust_vec_len, data_len),
|
||||
116 => wire__crate__bridge__api__rust_api_toggle_media_remove_audio_impl(port, ptr, rust_vec_len, data_len),
|
||||
117 => wire__crate__bridge__api__rust_api_try_request_contact_by_id_impl(port, ptr, rust_vec_len, data_len),
|
||||
118 => wire__crate__bridge__api__rust_api_unregister_home_widget_impl(port, ptr, rust_vec_len, data_len),
|
||||
119 => wire__crate__bridge__api__rust_api_update_fcm_token_impl(port, ptr, rust_vec_len, data_len),
|
||||
120 => wire__crate__bridge__api__rust_api_update_signed_pre_key_impl(port, ptr, rust_vec_len, data_len),
|
||||
121 => wire__crate__bridge__api__rust_api_upload_pqc_pre_keys_impl(port, ptr, rust_vec_len, data_len),
|
||||
122 => wire__crate__bridge__wrapper__app_database__rust_app_database_changes_impl(port, ptr, rust_vec_len, data_len),
|
||||
123 => wire__crate__bridge__wrapper__app_database__rust_app_database_execute_impl(port, ptr, rust_vec_len, data_len),
|
||||
124 => wire__crate__bridge__wrapper__app_database__rust_app_database_legacy_import_complete_impl(port, ptr, rust_vec_len, data_len),
|
||||
125 => wire__crate__bridge__wrapper__app_database__rust_app_database_migrate_legacy_database_impl(port, ptr, rust_vec_len, data_len),
|
||||
126 => wire__crate__bridge__wrapper__app_database__rust_app_database_select_impl(port, ptr, rust_vec_len, data_len),
|
||||
127 => wire__crate__bridge__wrapper__backup__rust_backup_archive_create_backup_archive_impl(port, ptr, rust_vec_len, data_len),
|
||||
128 => wire__crate__bridge__wrapper__backup__rust_backup_archive_get_backup_download_token_impl(port, ptr, rust_vec_len, data_len),
|
||||
129 => wire__crate__bridge__wrapper__backup__rust_backup_archive_restore_backup_archive_impl(port, ptr, rust_vec_len, data_len),
|
||||
130 => wire__crate__bridge__wrapper__backup__rust_backup_identity_get_backup_id_impl(port, ptr, rust_vec_len, data_len),
|
||||
131 => wire__crate__bridge__wrapper__backup__rust_backup_identity_get_backup_password_keys_impl(port, ptr, rust_vec_len, data_len),
|
||||
132 => wire__crate__bridge__wrapper__backup__rust_backup_identity_get_identity_backup_bytes_impl(port, ptr, rust_vec_len, data_len),
|
||||
133 => wire__crate__bridge__wrapper__backup__rust_backup_identity_import_backup_password_keys_impl(port, ptr, rust_vec_len, data_len),
|
||||
134 => wire__crate__bridge__wrapper__backup__rust_backup_identity_restore_identity_backup_impl(port, ptr, rust_vec_len, data_len),
|
||||
135 => wire__crate__bridge__wrapper__backup__rust_backup_identity_set_backup_password_keys_impl(port, ptr, rust_vec_len, data_len),
|
||||
136 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_decrypt_cloud_media_key_impl(port, ptr, rust_vec_len, data_len),
|
||||
137 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_encrypt_cloud_media_key_impl(port, ptr, rust_vec_len, data_len),
|
||||
138 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_get_signal_identity_impl(port, ptr, rust_vec_len, data_len),
|
||||
139 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_get_user_id_impl(port, ptr, rust_vec_len, data_len),
|
||||
140 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_import_serialized_impl(port, ptr, rust_vec_len, data_len),
|
||||
141 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_import_signal_identity_impl(port, ptr, rust_vec_len, data_len),
|
||||
142 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_remove_local_credentials_impl(port, ptr, rust_vec_len, data_len),
|
||||
143 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_serialize_impl(port, ptr, rust_vec_len, data_len),
|
||||
144 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_set_user_id_impl(port, ptr, rust_vec_len, data_len),
|
||||
145 => wire__crate__bridge__wrapper__signal__rust_signal_decrypt_impl(port, ptr, rust_vec_len, data_len),
|
||||
146 => wire__crate__bridge__wrapper__signal__rust_signal_encrypt_impl(port, ptr, rust_vec_len, data_len),
|
||||
147 => wire__crate__bridge__wrapper__signal__rust_signal_generate_bundle_impl(port, ptr, rust_vec_len, data_len),
|
||||
148 => wire__crate__bridge__wrapper__signal__rust_signal_generate_pqc_prekeys_impl(port, ptr, rust_vec_len, data_len),
|
||||
149 => wire__crate__bridge__wrapper__signal__rust_signal_get_contact_public_key_impl(port, ptr, rust_vec_len, data_len),
|
||||
150 => wire__crate__bridge__wrapper__signal__rust_signal_get_user_public_key_impl(port, ptr, rust_vec_len, data_len),
|
||||
151 => wire__crate__bridge__wrapper__signal__rust_signal_process_prekey_bundle_impl(port, ptr, rust_vec_len, data_len),
|
||||
152 => wire__crate__bridge__wrapper__rust_utils_generate_shares_impl(port, ptr, rust_vec_len, data_len),
|
||||
153 => wire__crate__bridge__wrapper__rust_utils_recover_secret_impl(port, ptr, rust_vec_len, data_len),
|
||||
154 => wire__crate__bridge__webxdc__send_update_impl(port, ptr, rust_vec_len, data_len),
|
||||
155 => wire__crate__bridge__webxdc__serve_impl(port, ptr, rust_vec_len, data_len),
|
||||
156 => wire__crate__bridge__groups__update_chat_deletion_time_impl(port, ptr, rust_vec_len, data_len),
|
||||
157 => wire__crate__bridge__groups__update_group_name_impl(port, ptr, rust_vec_len, data_len),
|
||||
158 => wire__crate__bridge__webxdc__updates_after_impl(port, ptr, rust_vec_len, data_len),
|
||||
160 => wire__crate__bridge__user_config__user_config_api_create_impl(port, ptr, rust_vec_len, data_len),
|
||||
161 => wire__crate__bridge__user_config__user_config_api_import_json_impl(port, ptr, rust_vec_len, data_len),
|
||||
162 => wire__crate__bridge__user_config__user_config_api_load_impl(port, ptr, rust_vec_len, data_len),
|
||||
163 => wire__crate__bridge__user_config__user_config_api_save_impl(port, ptr, rust_vec_len, data_len),
|
||||
164 => wire__crate__bridge__user_config__user_config_api_update_impl(port, ptr, rust_vec_len, data_len),
|
||||
4 => wire__crate__bridge__webxdc__cache_update_impl(port, ptr, rust_vec_len, data_len),
|
||||
5 => wire__crate__bridge__webxdc__catalog_impl(port, ptr, rust_vec_len, data_len),
|
||||
6 => wire__crate__bridge__logging__clean_log_file_impl(port, ptr, rust_vec_len, data_len),
|
||||
7 => wire__crate__bridge__logging__clear_log_file_impl(port, ptr, rust_vec_len, data_len),
|
||||
8 => wire__crate__bridge__webxdc__create_instance_impl(port, ptr, rust_vec_len, data_len),
|
||||
9 => wire__crate__bridge__groups__create_new_group_impl(port, ptr, rust_vec_len, data_len),
|
||||
10 => wire__crate__bridge__webxdc__delete_instance_impl(port, ptr, rust_vec_len, data_len),
|
||||
11 => wire__crate__bridge__groups__fetch_group_state_impl(port, ptr, rust_vec_len, data_len),
|
||||
12 => wire__crate__bridge__groups__fetch_group_states_for_unjoined_groups_impl(port, ptr, rust_vec_len, data_len),
|
||||
13 => wire__crate__bridge__groups__fetch_missing_group_public_keys_impl(port, ptr, rust_vec_len, data_len),
|
||||
14 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_change_exclusion_for_contact_impl(port, ptr, rust_vec_len, data_len),
|
||||
15 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_get_current_version_impl(port, ptr, rust_vec_len, data_len),
|
||||
16 => wire__crate__bridge__wrapper__user_discovery__flutter_user_discovery_update_verification_state_for_user_impl(port, ptr, rust_vec_len, data_len),
|
||||
17 => wire__crate__bridge__callbacks__init_flutter_callbacks_impl(port, ptr, rust_vec_len, data_len),
|
||||
18 => wire__crate__bridge__initialize_twonly_flutter_impl(port, ptr, rust_vec_len, data_len),
|
||||
19 => wire__crate__bridge__initialize_twonly_standalone_impl(port, ptr, rust_vec_len, data_len),
|
||||
20 => wire__crate__bridge__webxdc__instance_impl(port, ptr, rust_vec_len, data_len),
|
||||
21 => wire__crate__bridge__groups__leave_group_impl(port, ptr, rust_vec_len, data_len),
|
||||
22 => wire__crate__bridge__logging__load_log_file_impl(port, ptr, rust_vec_len, data_len),
|
||||
23 => wire__crate__bridge__groups__manage_admin_state_impl(port, ptr, rust_vec_len, data_len),
|
||||
24 => wire__crate__bridge__webxdc__members_impl(port, ptr, rust_vec_len, data_len),
|
||||
25 => wire__crate__bridge__webxdc__one_time_instances_impl(port, ptr, rust_vec_len, data_len),
|
||||
26 => wire__crate__bridge__webxdc__prepare_bundle_impl(port, ptr, rust_vec_len, data_len),
|
||||
27 => wire__crate__bridge__logging__read_last_log_lines_impl(port, ptr, rust_vec_len, data_len),
|
||||
28 => wire__crate__bridge__webxdc__refresh_catalog_impl(port, ptr, rust_vec_len, data_len),
|
||||
29 => wire__crate__bridge__groups__remove_member_from_group_impl(port, ptr, rust_vec_len, data_len),
|
||||
30 => wire__crate__bridge__api__rust_api_abandon_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
31 => wire__crate__bridge__api__rust_api_add_additional_user_impl(port, ptr, rust_vec_len, data_len),
|
||||
32 => wire__crate__bridge__api__rust_api_allocate_sequence_impl(port, ptr, rust_vec_len, data_len),
|
||||
34 => wire__crate__bridge__api__rust_api_authentication_headers_impl(port, ptr, rust_vec_len, data_len),
|
||||
36 => wire__crate__bridge__api__rust_api_change_username_impl(port, ptr, rust_vec_len, data_len),
|
||||
37 => wire__crate__bridge__api__rust_api_check_for_deleted_usernames_impl(port, ptr, rust_vec_len, data_len),
|
||||
38 => wire__crate__bridge__api__rust_api_check_for_passwordless_notification_impl(port, ptr, rust_vec_len, data_len),
|
||||
39 => wire__crate__bridge__api__rust_api_clear_contact_request_notifications_impl(port, ptr, rust_vec_len, data_len),
|
||||
40 => wire__crate__bridge__api__rust_api_clear_conversation_notifications_impl(port, ptr, rust_vec_len, data_len),
|
||||
41 => wire__crate__bridge__api__rust_api_close_impl(port, ptr, rust_vec_len, data_len),
|
||||
42 => wire__crate__bridge__api__rust_api_confirm_memories_upload_impl(port, ptr, rust_vec_len, data_len),
|
||||
43 => wire__crate__bridge__api__rust_api_connect_impl(port, ptr, rust_vec_len, data_len),
|
||||
44 => wire__crate__bridge__api__rust_api_connection_state_impl(port, ptr, rust_vec_len, data_len),
|
||||
45 => wire__crate__bridge__api__rust_api_crop_media_transparent_borders_impl(port, ptr, rust_vec_len, data_len),
|
||||
46 => wire__crate__bridge__api__rust_api_current_user_avatar_path_impl(port, ptr, rust_vec_len, data_len),
|
||||
48 => wire__crate__bridge__api__rust_api_delete_account_impl(port, ptr, rust_vec_len, data_len),
|
||||
49 => wire__crate__bridge__api__rust_api_delete_memory_impl(port, ptr, rust_vec_len, data_len),
|
||||
50 => wire__crate__bridge__api__rust_api_delete_widget_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
51 => wire__crate__bridge__api__rust_api_disable_memories_backup_impl(port, ptr, rust_vec_len, data_len),
|
||||
52 => wire__crate__bridge__api__rust_api_download_done_impl(port, ptr, rust_vec_len, data_len),
|
||||
53 => wire__crate__bridge__api__rust_api_download_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
54 => wire__crate__bridge__api__rust_api_download_pending_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
55 => wire__crate__bridge__api__rust_api_ensure_avatar_png_impl(port, ptr, rust_vec_len, data_len),
|
||||
56 => wire__crate__bridge__api__rust_api_establish_signal_session_impl(port, ptr, rust_vec_len, data_len),
|
||||
57 => wire__crate__bridge__api__rust_api_events_impl(port, ptr, rust_vec_len, data_len),
|
||||
58 => wire__crate__bridge__api__rust_api_finish_started_media_uploads_impl(port, ptr, rust_vec_len, data_len),
|
||||
59 => wire__crate__bridge__api__rust_api_force_ipa_check_impl(port, ptr, rust_vec_len, data_len),
|
||||
60 => wire__crate__bridge__api__rust_api_get_memories_url_impl(port, ptr, rust_vec_len, data_len),
|
||||
61 => wire__crate__bridge__api__rust_api_get_memories_usage_impl(port, ptr, rust_vec_len, data_len),
|
||||
62 => wire__crate__bridge__api__rust_api_get_plan_balance_impl(port, ptr, rust_vec_len, data_len),
|
||||
63 => wire__crate__bridge__api__rust_api_get_proof_of_work_impl(port, ptr, rust_vec_len, data_len),
|
||||
64 => wire__crate__bridge__api__rust_api_get_server_key_for_passwordless_recovery_impl(port, ptr, rust_vec_len, data_len),
|
||||
65 => wire__crate__bridge__api__rust_api_get_user_by_id_impl(port, ptr, rust_vec_len, data_len),
|
||||
66 => wire__crate__bridge__api__rust_api_get_user_data_impl(port, ptr, rust_vec_len, data_len),
|
||||
67 => wire__crate__bridge__api__rust_api_get_user_id_from_username_impl(port, ptr, rust_vec_len, data_len),
|
||||
68 => wire__crate__bridge__api__rust_api_hand_outbox_to_os_impl(port, ptr, rust_vec_len, data_len),
|
||||
69 => wire__crate__bridge__api__rust_api_initialize_media_upload_impl(port, ptr, rust_vec_len, data_len),
|
||||
70 => wire__crate__bridge__api__rust_api_insert_and_send_additional_data_impl(port, ptr, rust_vec_len, data_len),
|
||||
71 => wire__crate__bridge__api__rust_api_insert_and_send_ask_about_user_impl(port, ptr, rust_vec_len, data_len),
|
||||
72 => wire__crate__bridge__api__rust_api_insert_and_send_contact_share_impl(port, ptr, rust_vec_len, data_len),
|
||||
73 => wire__crate__bridge__api__rust_api_insert_and_send_text_impl(port, ptr, rust_vec_len, data_len),
|
||||
74 => wire__crate__bridge__api__rust_api_ipa_purchase_impl(port, ptr, rust_vec_len, data_len),
|
||||
75 => wire__crate__bridge__api__rust_api_load_plan_balance_impl(port, ptr, rust_vec_len, data_len),
|
||||
76 => wire__crate__bridge__api__rust_api_media_size_limit_report_impl(port, ptr, rust_vec_len, data_len),
|
||||
77 => wire__crate__bridge__api__rust_api_media_step_finished_impl(port, ptr, rust_vec_len, data_len),
|
||||
78 => wire__crate__bridge__api__rust_api_notification_badge_count_impl(port, ptr, rust_vec_len, data_len),
|
||||
79 => wire__crate__bridge__api__rust_api_notify_messages_opened_impl(port, ptr, rust_vec_len, data_len),
|
||||
80 => wire__crate__bridge__api__rust_api_perform_passwordless_recovery_heartbeat_impl(port, ptr, rust_vec_len, data_len),
|
||||
81 => wire__crate__bridge__api__rust_api_prerender_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
82 => wire__crate__bridge__api__rust_api_purge_media_temp_folder_impl(port, ptr, rust_vec_len, data_len),
|
||||
83 => wire__crate__bridge__api__rust_api_purge_widget_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
84 => wire__crate__bridge__api__rust_api_refresh_widget_manifest_impl(port, ptr, rust_vec_len, data_len),
|
||||
85 => wire__crate__bridge__api__rust_api_register_impl(port, ptr, rust_vec_len, data_len),
|
||||
86 => wire__crate__bridge__api__rust_api_register_home_widget_impl(port, ptr, rust_vec_len, data_len),
|
||||
87 => wire__crate__bridge__api__rust_api_register_passwordless_notification_impl(port, ptr, rust_vec_len, data_len),
|
||||
88 => wire__crate__bridge__api__rust_api_register_passwordless_recovery_impl(port, ptr, rust_vec_len, data_len),
|
||||
89 => wire__crate__bridge__api__rust_api_reload_configuration_impl(port, ptr, rust_vec_len, data_len),
|
||||
90 => wire__crate__bridge__api__rust_api_remove_additional_user_impl(port, ptr, rust_vec_len, data_len),
|
||||
91 => wire__crate__bridge__api__rust_api_remove_media_files_impl(port, ptr, rust_vec_len, data_len),
|
||||
92 => wire__crate__bridge__api__rust_api_report_user_impl(port, ptr, rust_vec_len, data_len),
|
||||
93 => wire__crate__bridge__api__rust_api_request_binary_impl(port, ptr, rust_vec_len, data_len),
|
||||
94 => wire__crate__bridge__api__rust_api_request_contact_by_username_impl(port, ptr, rust_vec_len, data_len),
|
||||
95 => wire__crate__bridge__api__rust_api_request_media_reupload_impl(port, ptr, rust_vec_len, data_len),
|
||||
96 => wire__crate__bridge__api__rust_api_request_memories_upload_impl(port, ptr, rust_vec_len, data_len),
|
||||
97 => wire__crate__bridge__api__rust_api_retransmit_all_messages_impl(port, ptr, rust_vec_len, data_len),
|
||||
98 => wire__crate__bridge__api__rust_api_retry_pending_media_reuploads_impl(port, ptr, rust_vec_len, data_len),
|
||||
99 => wire__crate__bridge__api__rust_api_reupload_pending_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
100 => wire__crate__bridge__api__rust_api_save_media_to_gallery_impl(port, ptr, rust_vec_len, data_len),
|
||||
101 => wire__crate__bridge__api__rust_api_send_binary_impl(port, ptr, rust_vec_len, data_len),
|
||||
102 => wire__crate__bridge__api__rust_api_send_contact_profile_impl(port, ptr, rust_vec_len, data_len),
|
||||
103 => wire__crate__bridge__api__rust_api_send_encrypted_content_impl(port, ptr, rust_vec_len, data_len),
|
||||
104 => wire__crate__bridge__api__rust_api_send_encrypted_content_to_group_impl(port, ptr, rust_vec_len, data_len),
|
||||
105 => wire__crate__bridge__api__rust_api_send_media_to_groups_impl(port, ptr, rust_vec_len, data_len),
|
||||
106 => wire__crate__bridge__api__rust_api_send_queued_message_impl(port, ptr, rust_vec_len, data_len),
|
||||
107 => wire__crate__bridge__api__rust_api_send_text_message_impl(port, ptr, rust_vec_len, data_len),
|
||||
108 => wire__crate__bridge__api__rust_api_send_typing_impl(port, ptr, rust_vec_len, data_len),
|
||||
109 => wire__crate__bridge__api__rust_api_set_background_impl(port, ptr, rust_vec_len, data_len),
|
||||
110 => wire__crate__bridge__api__rust_api_set_home_widget_groups_impl(port, ptr, rust_vec_len, data_len),
|
||||
111 => wire__crate__bridge__api__rust_api_set_login_token_impl(port, ptr, rust_vec_len, data_len),
|
||||
112 => wire__crate__bridge__api__rust_api_set_media_display_limit_impl(port, ptr, rust_vec_len, data_len),
|
||||
113 => wire__crate__bridge__api__rust_api_set_media_requires_authentication_impl(port, ptr, rust_vec_len, data_len),
|
||||
114 => wire__crate__bridge__api__rust_api_set_media_trim_impl(port, ptr, rust_vec_len, data_len),
|
||||
115 => wire__crate__bridge__api__rust_api_set_network_available_impl(port, ptr, rust_vec_len, data_len),
|
||||
116 => wire__crate__bridge__api__rust_api_store_media_impl(port, ptr, rust_vec_len, data_len),
|
||||
117 => wire__crate__bridge__api__rust_api_submit_recovery_share_impl(port, ptr, rust_vec_len, data_len),
|
||||
118 => wire__crate__bridge__api__rust_api_sync_widget_permissions_impl(port, ptr, rust_vec_len, data_len),
|
||||
119 => wire__crate__bridge__api__rust_api_toggle_media_remove_audio_impl(port, ptr, rust_vec_len, data_len),
|
||||
120 => wire__crate__bridge__api__rust_api_try_request_contact_by_id_impl(port, ptr, rust_vec_len, data_len),
|
||||
121 => wire__crate__bridge__api__rust_api_unregister_home_widget_impl(port, ptr, rust_vec_len, data_len),
|
||||
122 => wire__crate__bridge__api__rust_api_update_fcm_token_impl(port, ptr, rust_vec_len, data_len),
|
||||
123 => wire__crate__bridge__api__rust_api_update_signed_pre_key_impl(port, ptr, rust_vec_len, data_len),
|
||||
124 => wire__crate__bridge__api__rust_api_upload_pqc_pre_keys_impl(port, ptr, rust_vec_len, data_len),
|
||||
125 => wire__crate__bridge__wrapper__app_database__rust_app_database_changes_impl(port, ptr, rust_vec_len, data_len),
|
||||
126 => wire__crate__bridge__wrapper__app_database__rust_app_database_execute_impl(port, ptr, rust_vec_len, data_len),
|
||||
127 => wire__crate__bridge__wrapper__app_database__rust_app_database_legacy_import_complete_impl(port, ptr, rust_vec_len, data_len),
|
||||
128 => wire__crate__bridge__wrapper__app_database__rust_app_database_migrate_legacy_database_impl(port, ptr, rust_vec_len, data_len),
|
||||
129 => wire__crate__bridge__wrapper__app_database__rust_app_database_select_impl(port, ptr, rust_vec_len, data_len),
|
||||
130 => wire__crate__bridge__wrapper__backup__rust_backup_archive_create_backup_archive_impl(port, ptr, rust_vec_len, data_len),
|
||||
131 => wire__crate__bridge__wrapper__backup__rust_backup_archive_get_backup_download_token_impl(port, ptr, rust_vec_len, data_len),
|
||||
132 => wire__crate__bridge__wrapper__backup__rust_backup_archive_restore_backup_archive_impl(port, ptr, rust_vec_len, data_len),
|
||||
133 => wire__crate__bridge__wrapper__backup__rust_backup_identity_get_backup_id_impl(port, ptr, rust_vec_len, data_len),
|
||||
134 => wire__crate__bridge__wrapper__backup__rust_backup_identity_get_backup_password_keys_impl(port, ptr, rust_vec_len, data_len),
|
||||
135 => wire__crate__bridge__wrapper__backup__rust_backup_identity_get_identity_backup_bytes_impl(port, ptr, rust_vec_len, data_len),
|
||||
136 => wire__crate__bridge__wrapper__backup__rust_backup_identity_import_backup_password_keys_impl(port, ptr, rust_vec_len, data_len),
|
||||
137 => wire__crate__bridge__wrapper__backup__rust_backup_identity_restore_identity_backup_impl(port, ptr, rust_vec_len, data_len),
|
||||
138 => wire__crate__bridge__wrapper__backup__rust_backup_identity_set_backup_password_keys_impl(port, ptr, rust_vec_len, data_len),
|
||||
139 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_decrypt_cloud_media_key_impl(port, ptr, rust_vec_len, data_len),
|
||||
140 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_encrypt_cloud_media_key_impl(port, ptr, rust_vec_len, data_len),
|
||||
141 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_get_signal_identity_impl(port, ptr, rust_vec_len, data_len),
|
||||
142 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_get_user_id_impl(port, ptr, rust_vec_len, data_len),
|
||||
143 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_import_serialized_impl(port, ptr, rust_vec_len, data_len),
|
||||
144 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_import_signal_identity_impl(port, ptr, rust_vec_len, data_len),
|
||||
145 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_remove_local_credentials_impl(port, ptr, rust_vec_len, data_len),
|
||||
146 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_serialize_impl(port, ptr, rust_vec_len, data_len),
|
||||
147 => wire__crate__bridge__wrapper__key_manager__rust_key_manager_set_user_id_impl(port, ptr, rust_vec_len, data_len),
|
||||
148 => wire__crate__bridge__wrapper__signal__rust_signal_decrypt_impl(port, ptr, rust_vec_len, data_len),
|
||||
149 => wire__crate__bridge__wrapper__signal__rust_signal_encrypt_impl(port, ptr, rust_vec_len, data_len),
|
||||
150 => wire__crate__bridge__wrapper__signal__rust_signal_generate_bundle_impl(port, ptr, rust_vec_len, data_len),
|
||||
151 => wire__crate__bridge__wrapper__signal__rust_signal_generate_pqc_prekeys_impl(port, ptr, rust_vec_len, data_len),
|
||||
152 => wire__crate__bridge__wrapper__signal__rust_signal_get_contact_public_key_impl(port, ptr, rust_vec_len, data_len),
|
||||
153 => wire__crate__bridge__wrapper__signal__rust_signal_get_user_public_key_impl(port, ptr, rust_vec_len, data_len),
|
||||
154 => wire__crate__bridge__wrapper__signal__rust_signal_process_prekey_bundle_impl(port, ptr, rust_vec_len, data_len),
|
||||
155 => wire__crate__bridge__wrapper__rust_utils_generate_shares_impl(port, ptr, rust_vec_len, data_len),
|
||||
156 => wire__crate__bridge__wrapper__rust_utils_recover_secret_impl(port, ptr, rust_vec_len, data_len),
|
||||
157 => wire__crate__bridge__webxdc__send_update_impl(port, ptr, rust_vec_len, data_len),
|
||||
158 => wire__crate__bridge__webxdc__serve_impl(port, ptr, rust_vec_len, data_len),
|
||||
159 => wire__crate__bridge__groups__update_chat_deletion_time_impl(port, ptr, rust_vec_len, data_len),
|
||||
160 => wire__crate__bridge__groups__update_group_name_impl(port, ptr, rust_vec_len, data_len),
|
||||
161 => wire__crate__bridge__webxdc__updates_after_impl(port, ptr, rust_vec_len, data_len),
|
||||
163 => wire__crate__bridge__user_config__user_config_api_create_impl(port, ptr, rust_vec_len, data_len),
|
||||
164 => wire__crate__bridge__user_config__user_config_api_import_json_impl(port, ptr, rust_vec_len, data_len),
|
||||
165 => wire__crate__bridge__user_config__user_config_api_load_impl(port, ptr, rust_vec_len, data_len),
|
||||
166 => wire__crate__bridge__user_config__user_config_api_save_impl(port, ptr, rust_vec_len, data_len),
|
||||
167 => wire__crate__bridge__user_config__user_config_api_update_impl(port, ptr, rust_vec_len, data_len),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
|
@ -7569,17 +7726,17 @@ fn pde_ffi_dispatcher_sync_impl(
|
|||
) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse {
|
||||
// Codec=Pde (Serialization + dispatch), see doc to use other codecs
|
||||
match func_id {
|
||||
30 => wire__crate__bridge__api__rust_api_api_base_url_impl(ptr, rust_vec_len, data_len),
|
||||
32 => wire__crate__bridge__api__rust_api_avatar_png_path_impl(ptr, rust_vec_len, data_len),
|
||||
44 => {
|
||||
33 => wire__crate__bridge__api__rust_api_api_base_url_impl(ptr, rust_vec_len, data_len),
|
||||
35 => wire__crate__bridge__api__rust_api_avatar_png_path_impl(ptr, rust_vec_len, data_len),
|
||||
47 => {
|
||||
wire__crate__bridge__api__rust_api_decode_avatar_svg_impl(ptr, rust_vec_len, data_len)
|
||||
}
|
||||
159 => wire__crate__bridge__user_config__user_config_api_clone_impl(
|
||||
162 => wire__crate__bridge__user_config__user_config_api_clone_impl(
|
||||
ptr,
|
||||
rust_vec_len,
|
||||
data_len,
|
||||
),
|
||||
165 => wire__crate__bridge__logging__write_log_impl(ptr, rust_vec_len, data_len),
|
||||
168 => wire__crate__bridge__logging__write_log_impl(ptr, rust_vec_len, data_len),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
|
@ -8528,6 +8685,30 @@ impl flutter_rust_bridge::IntoIntoDart<crate::bridge::webxdc::WebxdcInstanceInfo
|
|||
}
|
||||
}
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
impl flutter_rust_bridge::IntoDart for crate::bridge::webxdc::WebxdcOneTimeInstance {
|
||||
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
|
||||
[
|
||||
self.instance_id.into_into_dart().into_dart(),
|
||||
self.name.into_into_dart().into_dart(),
|
||||
self.icon.into_into_dart().into_dart(),
|
||||
self.summary.into_into_dart().into_dart(),
|
||||
self.document.into_into_dart().into_dart(),
|
||||
]
|
||||
.into_dart()
|
||||
}
|
||||
}
|
||||
impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive
|
||||
for crate::bridge::webxdc::WebxdcOneTimeInstance
|
||||
{
|
||||
}
|
||||
impl flutter_rust_bridge::IntoIntoDart<crate::bridge::webxdc::WebxdcOneTimeInstance>
|
||||
for crate::bridge::webxdc::WebxdcOneTimeInstance
|
||||
{
|
||||
fn into_into_dart(self) -> crate::bridge::webxdc::WebxdcOneTimeInstance {
|
||||
self
|
||||
}
|
||||
}
|
||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||
impl flutter_rust_bridge::IntoDart for crate::bridge::webxdc::WebxdcResponse {
|
||||
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
|
||||
[
|
||||
|
|
@ -8562,6 +8743,9 @@ impl flutter_rust_bridge::IntoDart for crate::bridge::webxdc::WebxdcStoreApp {
|
|||
self.source_code_url.into_into_dart().into_dart(),
|
||||
self.icon.into_into_dart().into_dart(),
|
||||
self.bundle_bytes.into_into_dart().into_dart(),
|
||||
self.pro_only.into_into_dart().into_dart(),
|
||||
self.one_time.into_into_dart().into_dart(),
|
||||
self.instance_id.into_into_dart().into_dart(),
|
||||
]
|
||||
.into_dart()
|
||||
}
|
||||
|
|
@ -9047,6 +9231,16 @@ impl SseEncode for Vec<crate::database::app::SqlValue> {
|
|||
}
|
||||
}
|
||||
|
||||
impl SseEncode for Vec<crate::bridge::webxdc::WebxdcOneTimeInstance> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
<i32>::sse_encode(self.len() as _, serializer);
|
||||
for item in self {
|
||||
<crate::bridge::webxdc::WebxdcOneTimeInstance>::sse_encode(item, serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for Vec<crate::bridge::webxdc::WebxdcStoreApp> {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
|
|
@ -9570,6 +9764,17 @@ impl SseEncode for crate::bridge::webxdc::WebxdcInstanceInfo {
|
|||
}
|
||||
}
|
||||
|
||||
impl SseEncode for crate::bridge::webxdc::WebxdcOneTimeInstance {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
<String>::sse_encode(self.instance_id, serializer);
|
||||
<String>::sse_encode(self.name, serializer);
|
||||
<Option<Vec<u8>>>::sse_encode(self.icon, serializer);
|
||||
<Option<String>>::sse_encode(self.summary, serializer);
|
||||
<Option<String>>::sse_encode(self.document, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
impl SseEncode for crate::bridge::webxdc::WebxdcResponse {
|
||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||
|
|
@ -9591,6 +9796,9 @@ impl SseEncode for crate::bridge::webxdc::WebxdcStoreApp {
|
|||
<Option<String>>::sse_encode(self.source_code_url, serializer);
|
||||
<Option<Vec<u8>>>::sse_encode(self.icon, serializer);
|
||||
<i64>::sse_encode(self.bundle_bytes, serializer);
|
||||
<bool>::sse_encode(self.pro_only, serializer);
|
||||
<bool>::sse_encode(self.one_time, serializer);
|
||||
<Option<String>>::sse_encode(self.instance_id, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ impl MessageService {
|
|||
|| content.media.is_some()
|
||||
|| content.text_message.is_some()
|
||||
{
|
||||
sqlx::query!("UPDATE groups SET last_message_exchange = CAST(strftime('%s','now') AS INTEGER) WHERE group_id = ?", group_id)
|
||||
sqlx::query!("UPDATE groups SET last_message_exchange = CAST(strftime('%s','now') AS INTEGER), deleted_content = 0 WHERE group_id = ?", group_id)
|
||||
.execute(&database.pool).await?;
|
||||
}
|
||||
let members = sqlx::query_scalar!(
|
||||
|
|
@ -412,6 +412,8 @@ impl MessageService {
|
|||
webxdc_app: None,
|
||||
webxdc_update: None,
|
||||
webxdc_origin: None,
|
||||
webxdc_sync: None,
|
||||
webxdc_sync_request: None,
|
||||
}
|
||||
.encode_to_vec();
|
||||
self.insert_and_send_additional_data(group_id, "contacts".into(), data, false)
|
||||
|
|
@ -447,6 +449,8 @@ impl MessageService {
|
|||
webxdc_app: None,
|
||||
webxdc_update: None,
|
||||
webxdc_origin: None,
|
||||
webxdc_sync: None,
|
||||
webxdc_sync_request: None,
|
||||
}
|
||||
.encode_to_vec();
|
||||
self.insert_and_send_additional_data(group_id, "askAboutUser".into(), data, false)
|
||||
|
|
|
|||
|
|
@ -31,12 +31,30 @@ static DE_TRANSLATIONS: LazyLock<HashMap<String, String>> =
|
|||
/// The update itself is hidden -- app state rather than something a person
|
||||
/// sent -- but an `info` is written to be read, and becomes a chat row of its
|
||||
/// own on arrival. That row is what the receiver is woken and notified for.
|
||||
fn webxdc_announcement(message: &encrypted_content::AdditionalDataMessage) -> Option<String> {
|
||||
fn webxdc_announcement(
|
||||
message: &encrypted_content::AdditionalDataMessage,
|
||||
user_id: i64,
|
||||
) -> Option<String> {
|
||||
let data = <proto::AdditionalMessageData as prost::Message>::decode(
|
||||
message.additional_message_data.as_deref()?,
|
||||
)
|
||||
.ok()?;
|
||||
crate::services::webxdc::announcement(&data.webxdc_update?)
|
||||
let update = data.webxdc_update?;
|
||||
// Keep chat announcements shared, but only wake recipients selected by the app.
|
||||
let announcement = crate::services::webxdc::announcement(&update)?;
|
||||
match update.notify.as_deref() {
|
||||
None => Some(announcement),
|
||||
Some(raw) => {
|
||||
let recipients: std::collections::BTreeMap<String, String> =
|
||||
serde_json::from_str(raw).ok()?;
|
||||
let address =
|
||||
crate::services::webxdc::WebxdcService::address_for(&update.instance_id, user_id);
|
||||
let text = recipients.get(&address)?;
|
||||
let mut notification = update;
|
||||
notification.info = Some(text.clone());
|
||||
crate::services::webxdc::announcement(¬ification)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Who wrote the message with this id, as this device has it.
|
||||
|
|
@ -85,7 +103,9 @@ pub(crate) async fn should_wake_receiver(
|
|||
|| content
|
||||
.additional_data_message
|
||||
.as_ref()
|
||||
.is_some_and(|message| !message.hidden || webxdc_announcement(message).is_some())
|
||||
.is_some_and(|message| {
|
||||
!message.hidden || webxdc_announcement(message, target_user_id).is_some()
|
||||
})
|
||||
|| content.group_create.is_some()
|
||||
|| content.media.as_ref().is_some_and(|value| {
|
||||
// Widget media lands already opened and is deliberately recorded
|
||||
|
|
@ -199,6 +219,7 @@ impl NotificationDraft {
|
|||
/// transaction that commits that message. Transport retries are deduplicated
|
||||
/// by the receipt-derived event ID.
|
||||
pub(crate) async fn record_incoming_event(
|
||||
ctx: &Context,
|
||||
transaction: &mut Transaction<'_, Sqlite>,
|
||||
from_user_id: i64,
|
||||
receipt_id: &str,
|
||||
|
|
@ -270,9 +291,10 @@ pub(crate) async fn record_incoming_event(
|
|||
// announcement -- and only once the row it materialised into exists,
|
||||
// which it does not when the update was dropped for an unknown
|
||||
// instance, a mismatched chat, or an app that is over budget.
|
||||
let self_id = UserConfig::load_required_from(ctx)?.user_id;
|
||||
let announcement = message
|
||||
.hidden
|
||||
.then(|| webxdc_announcement(message))
|
||||
.then(|| webxdc_announcement(message, self_id))
|
||||
.flatten();
|
||||
let message_id = if message.hidden {
|
||||
crate::services::webxdc::WebxdcService::info_message_id(&message.sender_message_id)
|
||||
|
|
@ -842,7 +864,7 @@ mod tests {
|
|||
}
|
||||
|
||||
fn announcement_of(content: &proto::EncryptedContent) -> Option<String> {
|
||||
webxdc_announcement(content.additional_data_message.as_ref().unwrap())
|
||||
webxdc_announcement(content.additional_data_message.as_ref().unwrap(), 42)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -857,6 +879,62 @@ mod tests {
|
|||
assert!(announcement_of(&webxdc_update(Some(" \u{200e} "))).is_none());
|
||||
}
|
||||
|
||||
fn targeted_update(notify: &str) -> proto::EncryptedContent {
|
||||
let mut content = webxdc_update(Some("Dinner added"));
|
||||
let message = content.additional_data_message.as_mut().unwrap();
|
||||
let mut data = <proto::AdditionalMessageData as prost::Message>::decode(
|
||||
message.additional_message_data.as_deref().unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
data.webxdc_update.as_mut().unwrap().notify = Some(notify.to_owned());
|
||||
message.additional_message_data = Some(prost::Message::encode_to_vec(&data));
|
||||
content
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn expense_notifications_only_wake_and_record_affected_members() -> Result<()> {
|
||||
let directory = tempfile::tempdir()?;
|
||||
let ctx =
|
||||
Context::init_for_testing(directory.path().join("db"), directory.path().join("data"))
|
||||
.await?;
|
||||
UserConfig::save_json(
|
||||
&ctx,
|
||||
r#"{"userId":42,"username":"ben","displayName":"Ben"}"#,
|
||||
)?;
|
||||
let database = ctx.app_db.read().await.clone();
|
||||
let mut tx = database.pool.begin().await?;
|
||||
sqlx::query("INSERT INTO contacts(user_id, username) VALUES (7, 'anna')")
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
sqlx::query("INSERT INTO groups(group_id, group_name) VALUES ('g', 'Trip')")
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
sqlx::query("INSERT INTO messages(message_id, group_id, sender_id, type, content) VALUES ('message-info', 'g', 7, 'text', 'Dinner added')").execute(&mut *tx).await?;
|
||||
let address = crate::services::webxdc::WebxdcService::address_for("instance", 42);
|
||||
let raw = serde_json::json!({address: "You share dinner: €20"}).to_string();
|
||||
let content = targeted_update(&raw);
|
||||
assert!(should_wake_receiver(&mut tx, 42, &content).await?);
|
||||
assert!(!should_wake_receiver(&mut tx, 43, &content).await?);
|
||||
record_incoming_event(&ctx, &mut tx, 7, "included", &content).await?;
|
||||
record_incoming_event(&ctx, &mut tx, 7, "included", &content).await?;
|
||||
for raw in ["{}", "null", "not JSON", r#"{"someone-else":"Dinner"}"#] {
|
||||
let content = targeted_update(raw);
|
||||
assert!(!should_wake_receiver(&mut tx, 42, &content).await?);
|
||||
record_incoming_event(&ctx, &mut tx, 7, "excluded", &content).await?;
|
||||
}
|
||||
let rows: Vec<(String, String)> =
|
||||
sqlx::query_as("SELECT event_id, content FROM notification_outbox")
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
assert_eq!(
|
||||
rows,
|
||||
vec![("included".into(), "You share dinner: €20".into())]
|
||||
);
|
||||
// Older apps omit notify and retain their existing announcements.
|
||||
assert!(should_wake_receiver(&mut tx, 43, &webxdc_update(Some("Your turn"))).await?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pending_row(kind: &str) -> PendingRow {
|
||||
PendingRow {
|
||||
event_id: "event".into(),
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -134,7 +134,8 @@ impl WebxdcStore {
|
|||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
for entry in &catalog.entries {
|
||||
for (position, entry) in catalog.entries.iter().enumerate() {
|
||||
let sort_order = position as i64;
|
||||
if !Self::entry_is_sane(entry) {
|
||||
tracing::warn!(app_id = entry.app_id, "skipping unusable catalog entry");
|
||||
continue;
|
||||
|
|
@ -151,8 +152,8 @@ impl WebxdcStore {
|
|||
r#"INSERT INTO webxdc_apps
|
||||
(app_id, version, name, name_translations, source_code_url,
|
||||
description, icon, bundle_sha256, bundle_bytes, published,
|
||||
cached_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)
|
||||
cached_at, pro_only, sort_order, one_time)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?)
|
||||
ON CONFLICT(app_id, version) DO UPDATE SET
|
||||
name = excluded.name,
|
||||
name_translations = excluded.name_translations,
|
||||
|
|
@ -162,6 +163,9 @@ impl WebxdcStore {
|
|||
bundle_sha256 = excluded.bundle_sha256,
|
||||
bundle_bytes = excluded.bundle_bytes,
|
||||
published = 1,
|
||||
pro_only = excluded.pro_only,
|
||||
sort_order = excluded.sort_order,
|
||||
one_time = excluded.one_time,
|
||||
cached_at = excluded.cached_at"#,
|
||||
entry.app_id,
|
||||
entry.version,
|
||||
|
|
@ -173,6 +177,9 @@ impl WebxdcStore {
|
|||
entry.bundle_sha256,
|
||||
entry.bundle_bytes,
|
||||
now,
|
||||
entry.pro_only,
|
||||
sort_order,
|
||||
entry.one_time,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
|
|
|||
|
|
@ -97,6 +97,10 @@
|
|||
sendUpdateInterval: init.sendUpdateInterval,
|
||||
sendUpdateMaxSize: init.sendUpdateMaxSize,
|
||||
|
||||
getMembers: function () {
|
||||
return call('getMembers');
|
||||
},
|
||||
|
||||
sendUpdate: function (update, descr) {
|
||||
return call('sendUpdate', {
|
||||
payload: update.payload === undefined ? null : update.payload,
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ UserConfig testUserConfig({
|
|||
currentSignedPreKeyIndexStart: 100000,
|
||||
hideChangeLog: true,
|
||||
hideMemoriesBackupPromo: false,
|
||||
hideWidgetShareExplainer: false,
|
||||
updateFcmToken: true,
|
||||
canUseLoginTokenForAuth: true,
|
||||
isBackupEnabled: false,
|
||||
|
|
|
|||
|
|
@ -125,4 +125,32 @@ void main() {
|
|||
expect(remainingIds.contains('msg_d_sender_deleted_expired'), isFalse);
|
||||
},
|
||||
);
|
||||
|
||||
test('deleting a chat retains only one-time app state', () async {
|
||||
await twonlyDB.groupsDao.createNewGroup(
|
||||
GroupsCompanion.insert(groupId: 'chat', groupName: 'Trip'),
|
||||
);
|
||||
for (final statement in [
|
||||
"INSERT INTO messages(message_id, group_id, type) VALUES ('text', 'chat', 'text')",
|
||||
"INSERT INTO messages(message_id, group_id, type) VALUES ('expenses-card', 'chat', 'webxdcApp')",
|
||||
"INSERT INTO messages(message_id, group_id, type) VALUES ('game-card', 'chat', 'webxdcApp')",
|
||||
"INSERT INTO webxdc_apps(app_id, version, name, bundle_sha256, bundle_bytes, cached_at, one_time) VALUES ('expenses', 1, 'Expenses', 'expense-hash', 1, 0, 1)",
|
||||
"INSERT INTO webxdc_apps(app_id, version, name, bundle_sha256, bundle_bytes, cached_at, one_time) VALUES ('game', 1, 'Game', 'game-hash', 1, 0, 0)",
|
||||
"INSERT INTO webxdc_instances(instance_id, group_id, app_id, version, origin_token, created_at, last_update_at) VALUES ('expenses-card', 'chat', 'expenses', 1, 'expenses-origin', 0, 0)",
|
||||
"INSERT INTO webxdc_instances(instance_id, group_id, app_id, version, origin_token, created_at, last_update_at) VALUES ('game-card', 'chat', 'game', 1, 'game-origin', 0, 0)",
|
||||
"INSERT INTO webxdc_updates(instance_id, serial, message_id, payload, received_at) VALUES ('expenses-card', 1, 'expense-update', '{}', 0)",
|
||||
]) {
|
||||
await twonlyDB.customStatement(statement);
|
||||
}
|
||||
|
||||
await twonlyDB.messagesDao.deleteMessagesByGroupId('chat');
|
||||
|
||||
final messages = await twonlyDB.select(twonlyDB.messages).get();
|
||||
expect(messages.map((message) => message.messageId), ['expenses-card']);
|
||||
final instances = await twonlyDB.select(twonlyDB.webxdcInstances).get();
|
||||
expect(instances.map((instance) => instance.instanceId), ['expenses-card']);
|
||||
final updates = await twonlyDB.select(twonlyDB.webxdcUpdates).get();
|
||||
expect(updates, hasLength(1));
|
||||
expect((await twonlyDB.groupsDao.getGroup('chat'))?.deletedContent, isTrue);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue