mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 09:28:41 +00:00
fix #289
This commit is contained in:
parent
07d36c133c
commit
8d45c8e9ce
24 changed files with 588 additions and 181 deletions
|
|
@ -229,7 +229,7 @@ func getPushNotificationText(pushNotification: PushNotification) -> (String, Str
|
|||
.reactionToText: "hat mit {{content}} auf deinen Text reagiert.",
|
||||
.reactionToImage: "hat mit {{content}} auf dein Bild reagiert.",
|
||||
.response: "hat dir{inGroup} geantwortet.",
|
||||
.addedToGroup: "hat dich zu \"{{content}}\" hinzugefügt."
|
||||
.addedToGroup: "hat dich zu \"{{content}}\" hinzugefügt.",
|
||||
]
|
||||
} else { // Default to English
|
||||
pushNotificationText = [
|
||||
|
|
@ -247,7 +247,7 @@ func getPushNotificationText(pushNotification: PushNotification) -> (String, Str
|
|||
.reactionToText: "has reacted with {{content}} to your text.",
|
||||
.reactionToImage: "has reacted with {{content}} to your image.",
|
||||
.response: "has responded{inGroup}.",
|
||||
.addedToGroup: "has added you to \"{{content}}\""
|
||||
.addedToGroup: "has added you to \"{{content}}\"",
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -257,9 +257,10 @@ func getPushNotificationText(pushNotification: PushNotification) -> (String, Str
|
|||
content.replace("{{content}}", with: pushNotification.additionalContent)
|
||||
content.replace("{inGroup}", with: " in {inGroup}")
|
||||
content.replace("{inGroup}", with: pushNotification.additionalContent)
|
||||
} else {
|
||||
content.replace("{inGroup}", with: "")
|
||||
}
|
||||
|
||||
// Return the corresponding message or an empty string if not found
|
||||
return (content, title)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
|
|||
return entry != null;
|
||||
}
|
||||
|
||||
Future<void> deleteGroup(String groupId) async {
|
||||
await (delete(groups)..where((t) => t.groupId.equals(groupId))).go();
|
||||
}
|
||||
|
||||
Future<void> updateGroup(
|
||||
String groupId,
|
||||
GroupsCompanion updates,
|
||||
|
|
@ -42,7 +46,8 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
|
|||
..where(
|
||||
(t) =>
|
||||
t.groupId.equals(groupId) &
|
||||
t.memberState.equals(MemberState.leftGroup.name).not(),
|
||||
(t.memberState.equals(MemberState.leftGroup.name).not() |
|
||||
t.memberState.isNull()),
|
||||
))
|
||||
.get();
|
||||
}
|
||||
|
|
@ -131,8 +136,9 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
|
|||
|
||||
Future<Group?> _insertGroup(GroupsCompanion group) async {
|
||||
try {
|
||||
final rowId = await into(groups).insert(group);
|
||||
return await (select(groups)..where((t) => t.rowId.equals(rowId)))
|
||||
await into(groups).insert(group);
|
||||
return await (select(groups)
|
||||
..where((t) => t.groupId.equals(group.groupId.value)))
|
||||
.getSingle();
|
||||
} catch (e) {
|
||||
Log.error('Could not insert group: $e');
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ enum GroupActionType {
|
|||
promoteToAdmin,
|
||||
demoteToMember,
|
||||
updatedGroupName,
|
||||
changeDisplayMaxTime,
|
||||
}
|
||||
|
||||
@DataClassName('GroupHistory')
|
||||
|
|
@ -94,6 +95,8 @@ class GroupHistories extends Table {
|
|||
TextColumn get oldGroupName => text().nullable()();
|
||||
TextColumn get newGroupName => text().nullable()();
|
||||
|
||||
IntColumn get newDeleteMessagesAfterMilliseconds => integer().nullable()();
|
||||
|
||||
TextColumn get type => textEnum<GroupActionType>()();
|
||||
|
||||
DateTimeColumn get actionAt => dateTime().withDefault(currentDateAndTime)();
|
||||
|
|
|
|||
|
|
@ -7038,6 +7038,13 @@ class $GroupHistoriesTable extends GroupHistories
|
|||
late final GeneratedColumn<String> newGroupName = GeneratedColumn<String>(
|
||||
'new_group_name', aliasedName, true,
|
||||
type: DriftSqlType.string, requiredDuringInsert: false);
|
||||
static const VerificationMeta _newDeleteMessagesAfterMillisecondsMeta =
|
||||
const VerificationMeta('newDeleteMessagesAfterMilliseconds');
|
||||
@override
|
||||
late final GeneratedColumn<int> newDeleteMessagesAfterMilliseconds =
|
||||
GeneratedColumn<int>(
|
||||
'new_delete_messages_after_milliseconds', aliasedName, true,
|
||||
type: DriftSqlType.int, requiredDuringInsert: false);
|
||||
@override
|
||||
late final GeneratedColumnWithTypeConverter<GroupActionType, String> type =
|
||||
GeneratedColumn<String>('type', aliasedName, false,
|
||||
|
|
@ -7059,6 +7066,7 @@ class $GroupHistoriesTable extends GroupHistories
|
|||
affectedContactId,
|
||||
oldGroupName,
|
||||
newGroupName,
|
||||
newDeleteMessagesAfterMilliseconds,
|
||||
type,
|
||||
actionAt
|
||||
];
|
||||
|
|
@ -7108,6 +7116,13 @@ class $GroupHistoriesTable extends GroupHistories
|
|||
newGroupName.isAcceptableOrUnknown(
|
||||
data['new_group_name']!, _newGroupNameMeta));
|
||||
}
|
||||
if (data.containsKey('new_delete_messages_after_milliseconds')) {
|
||||
context.handle(
|
||||
_newDeleteMessagesAfterMillisecondsMeta,
|
||||
newDeleteMessagesAfterMilliseconds.isAcceptableOrUnknown(
|
||||
data['new_delete_messages_after_milliseconds']!,
|
||||
_newDeleteMessagesAfterMillisecondsMeta));
|
||||
}
|
||||
if (data.containsKey('action_at')) {
|
||||
context.handle(_actionAtMeta,
|
||||
actionAt.isAcceptableOrUnknown(data['action_at']!, _actionAtMeta));
|
||||
|
|
@ -7133,6 +7148,9 @@ class $GroupHistoriesTable extends GroupHistories
|
|||
.read(DriftSqlType.string, data['${effectivePrefix}old_group_name']),
|
||||
newGroupName: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}new_group_name']),
|
||||
newDeleteMessagesAfterMilliseconds: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}new_delete_messages_after_milliseconds']),
|
||||
type: $GroupHistoriesTable.$convertertype.fromSql(attachedDatabase
|
||||
.typeMapping
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}type'])!),
|
||||
|
|
@ -7157,6 +7175,7 @@ class GroupHistory extends DataClass implements Insertable<GroupHistory> {
|
|||
final int? affectedContactId;
|
||||
final String? oldGroupName;
|
||||
final String? newGroupName;
|
||||
final int? newDeleteMessagesAfterMilliseconds;
|
||||
final GroupActionType type;
|
||||
final DateTime actionAt;
|
||||
const GroupHistory(
|
||||
|
|
@ -7166,6 +7185,7 @@ class GroupHistory extends DataClass implements Insertable<GroupHistory> {
|
|||
this.affectedContactId,
|
||||
this.oldGroupName,
|
||||
this.newGroupName,
|
||||
this.newDeleteMessagesAfterMilliseconds,
|
||||
required this.type,
|
||||
required this.actionAt});
|
||||
@override
|
||||
|
|
@ -7185,6 +7205,10 @@ class GroupHistory extends DataClass implements Insertable<GroupHistory> {
|
|||
if (!nullToAbsent || newGroupName != null) {
|
||||
map['new_group_name'] = Variable<String>(newGroupName);
|
||||
}
|
||||
if (!nullToAbsent || newDeleteMessagesAfterMilliseconds != null) {
|
||||
map['new_delete_messages_after_milliseconds'] =
|
||||
Variable<int>(newDeleteMessagesAfterMilliseconds);
|
||||
}
|
||||
{
|
||||
map['type'] =
|
||||
Variable<String>($GroupHistoriesTable.$convertertype.toSql(type));
|
||||
|
|
@ -7209,6 +7233,10 @@ class GroupHistory extends DataClass implements Insertable<GroupHistory> {
|
|||
newGroupName: newGroupName == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(newGroupName),
|
||||
newDeleteMessagesAfterMilliseconds:
|
||||
newDeleteMessagesAfterMilliseconds == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(newDeleteMessagesAfterMilliseconds),
|
||||
type: Value(type),
|
||||
actionAt: Value(actionAt),
|
||||
);
|
||||
|
|
@ -7224,6 +7252,8 @@ class GroupHistory extends DataClass implements Insertable<GroupHistory> {
|
|||
affectedContactId: serializer.fromJson<int?>(json['affectedContactId']),
|
||||
oldGroupName: serializer.fromJson<String?>(json['oldGroupName']),
|
||||
newGroupName: serializer.fromJson<String?>(json['newGroupName']),
|
||||
newDeleteMessagesAfterMilliseconds:
|
||||
serializer.fromJson<int?>(json['newDeleteMessagesAfterMilliseconds']),
|
||||
type: $GroupHistoriesTable.$convertertype
|
||||
.fromJson(serializer.fromJson<String>(json['type'])),
|
||||
actionAt: serializer.fromJson<DateTime>(json['actionAt']),
|
||||
|
|
@ -7239,6 +7269,8 @@ class GroupHistory extends DataClass implements Insertable<GroupHistory> {
|
|||
'affectedContactId': serializer.toJson<int?>(affectedContactId),
|
||||
'oldGroupName': serializer.toJson<String?>(oldGroupName),
|
||||
'newGroupName': serializer.toJson<String?>(newGroupName),
|
||||
'newDeleteMessagesAfterMilliseconds':
|
||||
serializer.toJson<int?>(newDeleteMessagesAfterMilliseconds),
|
||||
'type': serializer
|
||||
.toJson<String>($GroupHistoriesTable.$convertertype.toJson(type)),
|
||||
'actionAt': serializer.toJson<DateTime>(actionAt),
|
||||
|
|
@ -7252,6 +7284,7 @@ class GroupHistory extends DataClass implements Insertable<GroupHistory> {
|
|||
Value<int?> affectedContactId = const Value.absent(),
|
||||
Value<String?> oldGroupName = const Value.absent(),
|
||||
Value<String?> newGroupName = const Value.absent(),
|
||||
Value<int?> newDeleteMessagesAfterMilliseconds = const Value.absent(),
|
||||
GroupActionType? type,
|
||||
DateTime? actionAt}) =>
|
||||
GroupHistory(
|
||||
|
|
@ -7265,6 +7298,10 @@ class GroupHistory extends DataClass implements Insertable<GroupHistory> {
|
|||
oldGroupName.present ? oldGroupName.value : this.oldGroupName,
|
||||
newGroupName:
|
||||
newGroupName.present ? newGroupName.value : this.newGroupName,
|
||||
newDeleteMessagesAfterMilliseconds:
|
||||
newDeleteMessagesAfterMilliseconds.present
|
||||
? newDeleteMessagesAfterMilliseconds.value
|
||||
: this.newDeleteMessagesAfterMilliseconds,
|
||||
type: type ?? this.type,
|
||||
actionAt: actionAt ?? this.actionAt,
|
||||
);
|
||||
|
|
@ -7284,6 +7321,10 @@ class GroupHistory extends DataClass implements Insertable<GroupHistory> {
|
|||
newGroupName: data.newGroupName.present
|
||||
? data.newGroupName.value
|
||||
: this.newGroupName,
|
||||
newDeleteMessagesAfterMilliseconds:
|
||||
data.newDeleteMessagesAfterMilliseconds.present
|
||||
? data.newDeleteMessagesAfterMilliseconds.value
|
||||
: this.newDeleteMessagesAfterMilliseconds,
|
||||
type: data.type.present ? data.type.value : this.type,
|
||||
actionAt: data.actionAt.present ? data.actionAt.value : this.actionAt,
|
||||
);
|
||||
|
|
@ -7298,6 +7339,8 @@ class GroupHistory extends DataClass implements Insertable<GroupHistory> {
|
|||
..write('affectedContactId: $affectedContactId, ')
|
||||
..write('oldGroupName: $oldGroupName, ')
|
||||
..write('newGroupName: $newGroupName, ')
|
||||
..write(
|
||||
'newDeleteMessagesAfterMilliseconds: $newDeleteMessagesAfterMilliseconds, ')
|
||||
..write('type: $type, ')
|
||||
..write('actionAt: $actionAt')
|
||||
..write(')'))
|
||||
|
|
@ -7305,8 +7348,16 @@ class GroupHistory extends DataClass implements Insertable<GroupHistory> {
|
|||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(groupHistoryId, groupId, contactId,
|
||||
affectedContactId, oldGroupName, newGroupName, type, actionAt);
|
||||
int get hashCode => Object.hash(
|
||||
groupHistoryId,
|
||||
groupId,
|
||||
contactId,
|
||||
affectedContactId,
|
||||
oldGroupName,
|
||||
newGroupName,
|
||||
newDeleteMessagesAfterMilliseconds,
|
||||
type,
|
||||
actionAt);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
|
|
@ -7317,6 +7368,8 @@ class GroupHistory extends DataClass implements Insertable<GroupHistory> {
|
|||
other.affectedContactId == this.affectedContactId &&
|
||||
other.oldGroupName == this.oldGroupName &&
|
||||
other.newGroupName == this.newGroupName &&
|
||||
other.newDeleteMessagesAfterMilliseconds ==
|
||||
this.newDeleteMessagesAfterMilliseconds &&
|
||||
other.type == this.type &&
|
||||
other.actionAt == this.actionAt);
|
||||
}
|
||||
|
|
@ -7328,6 +7381,7 @@ class GroupHistoriesCompanion extends UpdateCompanion<GroupHistory> {
|
|||
final Value<int?> affectedContactId;
|
||||
final Value<String?> oldGroupName;
|
||||
final Value<String?> newGroupName;
|
||||
final Value<int?> newDeleteMessagesAfterMilliseconds;
|
||||
final Value<GroupActionType> type;
|
||||
final Value<DateTime> actionAt;
|
||||
final Value<int> rowid;
|
||||
|
|
@ -7338,6 +7392,7 @@ class GroupHistoriesCompanion extends UpdateCompanion<GroupHistory> {
|
|||
this.affectedContactId = const Value.absent(),
|
||||
this.oldGroupName = const Value.absent(),
|
||||
this.newGroupName = const Value.absent(),
|
||||
this.newDeleteMessagesAfterMilliseconds = const Value.absent(),
|
||||
this.type = const Value.absent(),
|
||||
this.actionAt = const Value.absent(),
|
||||
this.rowid = const Value.absent(),
|
||||
|
|
@ -7349,6 +7404,7 @@ class GroupHistoriesCompanion extends UpdateCompanion<GroupHistory> {
|
|||
this.affectedContactId = const Value.absent(),
|
||||
this.oldGroupName = const Value.absent(),
|
||||
this.newGroupName = const Value.absent(),
|
||||
this.newDeleteMessagesAfterMilliseconds = const Value.absent(),
|
||||
required GroupActionType type,
|
||||
this.actionAt = const Value.absent(),
|
||||
this.rowid = const Value.absent(),
|
||||
|
|
@ -7362,6 +7418,7 @@ class GroupHistoriesCompanion extends UpdateCompanion<GroupHistory> {
|
|||
Expression<int>? affectedContactId,
|
||||
Expression<String>? oldGroupName,
|
||||
Expression<String>? newGroupName,
|
||||
Expression<int>? newDeleteMessagesAfterMilliseconds,
|
||||
Expression<String>? type,
|
||||
Expression<DateTime>? actionAt,
|
||||
Expression<int>? rowid,
|
||||
|
|
@ -7373,6 +7430,9 @@ class GroupHistoriesCompanion extends UpdateCompanion<GroupHistory> {
|
|||
if (affectedContactId != null) 'affected_contact_id': affectedContactId,
|
||||
if (oldGroupName != null) 'old_group_name': oldGroupName,
|
||||
if (newGroupName != null) 'new_group_name': newGroupName,
|
||||
if (newDeleteMessagesAfterMilliseconds != null)
|
||||
'new_delete_messages_after_milliseconds':
|
||||
newDeleteMessagesAfterMilliseconds,
|
||||
if (type != null) 'type': type,
|
||||
if (actionAt != null) 'action_at': actionAt,
|
||||
if (rowid != null) 'rowid': rowid,
|
||||
|
|
@ -7386,6 +7446,7 @@ class GroupHistoriesCompanion extends UpdateCompanion<GroupHistory> {
|
|||
Value<int?>? affectedContactId,
|
||||
Value<String?>? oldGroupName,
|
||||
Value<String?>? newGroupName,
|
||||
Value<int?>? newDeleteMessagesAfterMilliseconds,
|
||||
Value<GroupActionType>? type,
|
||||
Value<DateTime>? actionAt,
|
||||
Value<int>? rowid}) {
|
||||
|
|
@ -7396,6 +7457,8 @@ class GroupHistoriesCompanion extends UpdateCompanion<GroupHistory> {
|
|||
affectedContactId: affectedContactId ?? this.affectedContactId,
|
||||
oldGroupName: oldGroupName ?? this.oldGroupName,
|
||||
newGroupName: newGroupName ?? this.newGroupName,
|
||||
newDeleteMessagesAfterMilliseconds: newDeleteMessagesAfterMilliseconds ??
|
||||
this.newDeleteMessagesAfterMilliseconds,
|
||||
type: type ?? this.type,
|
||||
actionAt: actionAt ?? this.actionAt,
|
||||
rowid: rowid ?? this.rowid,
|
||||
|
|
@ -7423,6 +7486,10 @@ class GroupHistoriesCompanion extends UpdateCompanion<GroupHistory> {
|
|||
if (newGroupName.present) {
|
||||
map['new_group_name'] = Variable<String>(newGroupName.value);
|
||||
}
|
||||
if (newDeleteMessagesAfterMilliseconds.present) {
|
||||
map['new_delete_messages_after_milliseconds'] =
|
||||
Variable<int>(newDeleteMessagesAfterMilliseconds.value);
|
||||
}
|
||||
if (type.present) {
|
||||
map['type'] = Variable<String>(
|
||||
$GroupHistoriesTable.$convertertype.toSql(type.value));
|
||||
|
|
@ -7445,6 +7512,8 @@ class GroupHistoriesCompanion extends UpdateCompanion<GroupHistory> {
|
|||
..write('affectedContactId: $affectedContactId, ')
|
||||
..write('oldGroupName: $oldGroupName, ')
|
||||
..write('newGroupName: $newGroupName, ')
|
||||
..write(
|
||||
'newDeleteMessagesAfterMilliseconds: $newDeleteMessagesAfterMilliseconds, ')
|
||||
..write('type: $type, ')
|
||||
..write('actionAt: $actionAt, ')
|
||||
..write('rowid: $rowid')
|
||||
|
|
@ -13359,6 +13428,7 @@ typedef $$GroupHistoriesTableCreateCompanionBuilder = GroupHistoriesCompanion
|
|||
Value<int?> affectedContactId,
|
||||
Value<String?> oldGroupName,
|
||||
Value<String?> newGroupName,
|
||||
Value<int?> newDeleteMessagesAfterMilliseconds,
|
||||
required GroupActionType type,
|
||||
Value<DateTime> actionAt,
|
||||
Value<int> rowid,
|
||||
|
|
@ -13371,6 +13441,7 @@ typedef $$GroupHistoriesTableUpdateCompanionBuilder = GroupHistoriesCompanion
|
|||
Value<int?> affectedContactId,
|
||||
Value<String?> oldGroupName,
|
||||
Value<String?> newGroupName,
|
||||
Value<int?> newDeleteMessagesAfterMilliseconds,
|
||||
Value<GroupActionType> type,
|
||||
Value<DateTime> actionAt,
|
||||
Value<int> rowid,
|
||||
|
|
@ -13445,6 +13516,11 @@ class $$GroupHistoriesTableFilterComposer
|
|||
ColumnFilters<String> get newGroupName => $composableBuilder(
|
||||
column: $table.newGroupName, builder: (column) => ColumnFilters(column));
|
||||
|
||||
ColumnFilters<int> get newDeleteMessagesAfterMilliseconds =>
|
||||
$composableBuilder(
|
||||
column: $table.newDeleteMessagesAfterMilliseconds,
|
||||
builder: (column) => ColumnFilters(column));
|
||||
|
||||
ColumnWithTypeConverterFilters<GroupActionType, GroupActionType, String>
|
||||
get type => $composableBuilder(
|
||||
column: $table.type,
|
||||
|
|
@ -13535,6 +13611,11 @@ class $$GroupHistoriesTableOrderingComposer
|
|||
column: $table.newGroupName,
|
||||
builder: (column) => ColumnOrderings(column));
|
||||
|
||||
ColumnOrderings<int> get newDeleteMessagesAfterMilliseconds =>
|
||||
$composableBuilder(
|
||||
column: $table.newDeleteMessagesAfterMilliseconds,
|
||||
builder: (column) => ColumnOrderings(column));
|
||||
|
||||
ColumnOrderings<String> get type => $composableBuilder(
|
||||
column: $table.type, builder: (column) => ColumnOrderings(column));
|
||||
|
||||
|
|
@ -13620,6 +13701,11 @@ class $$GroupHistoriesTableAnnotationComposer
|
|||
GeneratedColumn<String> get newGroupName => $composableBuilder(
|
||||
column: $table.newGroupName, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<int> get newDeleteMessagesAfterMilliseconds =>
|
||||
$composableBuilder(
|
||||
column: $table.newDeleteMessagesAfterMilliseconds,
|
||||
builder: (column) => column);
|
||||
|
||||
GeneratedColumnWithTypeConverter<GroupActionType, String> get type =>
|
||||
$composableBuilder(column: $table.type, builder: (column) => column);
|
||||
|
||||
|
|
@ -13717,6 +13803,8 @@ class $$GroupHistoriesTableTableManager extends RootTableManager<
|
|||
Value<int?> affectedContactId = const Value.absent(),
|
||||
Value<String?> oldGroupName = const Value.absent(),
|
||||
Value<String?> newGroupName = const Value.absent(),
|
||||
Value<int?> newDeleteMessagesAfterMilliseconds =
|
||||
const Value.absent(),
|
||||
Value<GroupActionType> type = const Value.absent(),
|
||||
Value<DateTime> actionAt = const Value.absent(),
|
||||
Value<int> rowid = const Value.absent(),
|
||||
|
|
@ -13728,6 +13816,8 @@ class $$GroupHistoriesTableTableManager extends RootTableManager<
|
|||
affectedContactId: affectedContactId,
|
||||
oldGroupName: oldGroupName,
|
||||
newGroupName: newGroupName,
|
||||
newDeleteMessagesAfterMilliseconds:
|
||||
newDeleteMessagesAfterMilliseconds,
|
||||
type: type,
|
||||
actionAt: actionAt,
|
||||
rowid: rowid,
|
||||
|
|
@ -13739,6 +13829,8 @@ class $$GroupHistoriesTableTableManager extends RootTableManager<
|
|||
Value<int?> affectedContactId = const Value.absent(),
|
||||
Value<String?> oldGroupName = const Value.absent(),
|
||||
Value<String?> newGroupName = const Value.absent(),
|
||||
Value<int?> newDeleteMessagesAfterMilliseconds =
|
||||
const Value.absent(),
|
||||
required GroupActionType type,
|
||||
Value<DateTime> actionAt = const Value.absent(),
|
||||
Value<int> rowid = const Value.absent(),
|
||||
|
|
@ -13750,6 +13842,8 @@ class $$GroupHistoriesTableTableManager extends RootTableManager<
|
|||
affectedContactId: affectedContactId,
|
||||
oldGroupName: oldGroupName,
|
||||
newGroupName: newGroupName,
|
||||
newDeleteMessagesAfterMilliseconds:
|
||||
newDeleteMessagesAfterMilliseconds,
|
||||
type: type,
|
||||
actionAt: actionAt,
|
||||
rowid: rowid,
|
||||
|
|
|
|||
|
|
@ -804,5 +804,7 @@
|
|||
"leaveGroupSelectOtherAdminBody": "Um die Gruppe zu verlassen, musst du zuerst einen neuen Administrator auswählen.",
|
||||
"leaveGroupSureTitle": "Gruppe verlassen",
|
||||
"leaveGroupSureBody": "Willst du die Gruppe wirklich verlassen?",
|
||||
"leaveGroupSureOkBtn": "Gruppe verlassen"
|
||||
"leaveGroupSureOkBtn": "Gruppe verlassen",
|
||||
"changeDisplayMaxTime": "{username} hat das Zeitlimit für verschwindende Nachrichten auf {time}.",
|
||||
"youChangedDisplayMaxTime": "Du hat das Zeitlimit für verschwindende Nachrichten auf {time}."
|
||||
}
|
||||
|
|
@ -582,5 +582,7 @@
|
|||
"leaveGroupSelectOtherAdminBody": "To leave the group, you must first select a new administrator.",
|
||||
"leaveGroupSureTitle": "Leave group",
|
||||
"leaveGroupSureBody": "Do you really want to leave the group?",
|
||||
"leaveGroupSureOkBtn": "Leave group"
|
||||
"leaveGroupSureOkBtn": "Leave group",
|
||||
"changeDisplayMaxTime": "{username} has set the time limit for disappearing messages to {time}.",
|
||||
"youChangedDisplayMaxTime": "You have set the time limit for disappearing messages to {time}."
|
||||
}
|
||||
|
|
@ -2599,6 +2599,18 @@ abstract class AppLocalizations {
|
|||
/// In en, this message translates to:
|
||||
/// **'Leave group'**
|
||||
String get leaveGroupSureOkBtn;
|
||||
|
||||
/// No description provided for @changeDisplayMaxTime.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{username} has set the time limit for disappearing messages to {time}.'**
|
||||
String changeDisplayMaxTime(Object time, Object username);
|
||||
|
||||
/// No description provided for @youChangedDisplayMaxTime.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'You have set the time limit for disappearing messages to {time}.'**
|
||||
String youChangedDisplayMaxTime(Object time);
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
|
|
|||
|
|
@ -1421,4 +1421,14 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get leaveGroupSureOkBtn => 'Gruppe verlassen';
|
||||
|
||||
@override
|
||||
String changeDisplayMaxTime(Object time, Object username) {
|
||||
return '$username hat das Zeitlimit für verschwindende Nachrichten auf $time.';
|
||||
}
|
||||
|
||||
@override
|
||||
String youChangedDisplayMaxTime(Object time) {
|
||||
return 'Du hat das Zeitlimit für verschwindende Nachrichten auf $time.';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1411,4 +1411,14 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get leaveGroupSureOkBtn => 'Leave group';
|
||||
|
||||
@override
|
||||
String changeDisplayMaxTime(Object time, Object username) {
|
||||
return '$username has set the time limit for disappearing messages to $time.';
|
||||
}
|
||||
|
||||
@override
|
||||
String youChangedDisplayMaxTime(Object time) {
|
||||
return 'You have set the time limit for disappearing messages to $time.';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -415,6 +415,7 @@ class EncryptedContent_GroupUpdate extends $pb.GeneratedMessage {
|
|||
$core.String? groupActionType,
|
||||
$fixnum.Int64? affectedContactId,
|
||||
$core.String? newGroupName,
|
||||
$fixnum.Int64? newDeleteMessagesAfterMilliseconds,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (groupActionType != null) {
|
||||
|
|
@ -426,6 +427,9 @@ class EncryptedContent_GroupUpdate extends $pb.GeneratedMessage {
|
|||
if (newGroupName != null) {
|
||||
$result.newGroupName = newGroupName;
|
||||
}
|
||||
if (newDeleteMessagesAfterMilliseconds != null) {
|
||||
$result.newDeleteMessagesAfterMilliseconds = newDeleteMessagesAfterMilliseconds;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
EncryptedContent_GroupUpdate._() : super();
|
||||
|
|
@ -436,6 +440,7 @@ class EncryptedContent_GroupUpdate extends $pb.GeneratedMessage {
|
|||
..aOS(1, _omitFieldNames ? '' : 'groupActionType', protoName: 'groupActionType')
|
||||
..aInt64(2, _omitFieldNames ? '' : 'affectedContactId', protoName: 'affectedContactId')
|
||||
..aOS(3, _omitFieldNames ? '' : 'newGroupName', protoName: 'newGroupName')
|
||||
..aInt64(4, _omitFieldNames ? '' : 'newDeleteMessagesAfterMilliseconds', protoName: 'newDeleteMessagesAfterMilliseconds')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
|
|
@ -486,6 +491,15 @@ class EncryptedContent_GroupUpdate extends $pb.GeneratedMessage {
|
|||
$core.bool hasNewGroupName() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearNewGroupName() => clearField(3);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$fixnum.Int64 get newDeleteMessagesAfterMilliseconds => $_getI64(3);
|
||||
@$pb.TagNumber(4)
|
||||
set newDeleteMessagesAfterMilliseconds($fixnum.Int64 v) { $_setInt64(3, v); }
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasNewDeleteMessagesAfterMilliseconds() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearNewDeleteMessagesAfterMilliseconds() => clearField(4);
|
||||
}
|
||||
|
||||
class EncryptedContent_TextMessage extends $pb.GeneratedMessage {
|
||||
|
|
|
|||
|
|
@ -170,10 +170,12 @@ const EncryptedContent_GroupUpdate$json = {
|
|||
{'1': 'groupActionType', '3': 1, '4': 1, '5': 9, '10': 'groupActionType'},
|
||||
{'1': 'affectedContactId', '3': 2, '4': 1, '5': 3, '9': 0, '10': 'affectedContactId', '17': true},
|
||||
{'1': 'newGroupName', '3': 3, '4': 1, '5': 9, '9': 1, '10': 'newGroupName', '17': true},
|
||||
{'1': 'newDeleteMessagesAfterMilliseconds', '3': 4, '4': 1, '5': 3, '9': 2, '10': 'newDeleteMessagesAfterMilliseconds', '17': true},
|
||||
],
|
||||
'8': [
|
||||
{'1': '_affectedContactId'},
|
||||
{'1': '_newGroupName'},
|
||||
{'1': '_newDeleteMessagesAfterMilliseconds'},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
@ -389,53 +391,55 @@ final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode(
|
|||
'dGVkQ29udGVudC5SZXNlbmRHcm91cFB1YmxpY0tleUgPUhRyZXNlbmRHcm91cFB1YmxpY0tleY'
|
||||
'gBARpRCgtHcm91cENyZWF0ZRIaCghzdGF0ZUtleRgDIAEoDFIIc3RhdGVLZXkSJgoOZ3JvdXBQ'
|
||||
'dWJsaWNLZXkYBCABKAxSDmdyb3VwUHVibGljS2V5GjMKCUdyb3VwSm9pbhImCg5ncm91cFB1Ym'
|
||||
'xpY0tleRgBIAEoDFIOZ3JvdXBQdWJsaWNLZXkaFgoUUmVzZW5kR3JvdXBQdWJsaWNLZXkaugEK'
|
||||
'xpY0tleRgBIAEoDFIOZ3JvdXBQdWJsaWNLZXkaFgoUUmVzZW5kR3JvdXBQdWJsaWNLZXkatgIK'
|
||||
'C0dyb3VwVXBkYXRlEigKD2dyb3VwQWN0aW9uVHlwZRgBIAEoCVIPZ3JvdXBBY3Rpb25UeXBlEj'
|
||||
'EKEWFmZmVjdGVkQ29udGFjdElkGAIgASgDSABSEWFmZmVjdGVkQ29udGFjdElkiAEBEicKDG5l'
|
||||
'd0dyb3VwTmFtZRgDIAEoCUgBUgxuZXdHcm91cE5hbWWIAQFCFAoSX2FmZmVjdGVkQ29udGFjdE'
|
||||
'lkQg8KDV9uZXdHcm91cE5hbWUaqQEKC1RleHRNZXNzYWdlEigKD3NlbmRlck1lc3NhZ2VJZBgB'
|
||||
'IAEoCVIPc2VuZGVyTWVzc2FnZUlkEhIKBHRleHQYAiABKAlSBHRleHQSHAoJdGltZXN0YW1wGA'
|
||||
'MgASgDUgl0aW1lc3RhbXASKwoOcXVvdGVNZXNzYWdlSWQYBCABKAlIAFIOcXVvdGVNZXNzYWdl'
|
||||
'SWSIAQFCEQoPX3F1b3RlTWVzc2FnZUlkGmIKCFJlYWN0aW9uEigKD3RhcmdldE1lc3NhZ2VJZB'
|
||||
'gBIAEoCVIPdGFyZ2V0TWVzc2FnZUlkEhQKBWVtb2ppGAIgASgJUgVlbW9qaRIWCgZyZW1vdmUY'
|
||||
'AyABKAhSBnJlbW92ZRq3AgoNTWVzc2FnZVVwZGF0ZRI4CgR0eXBlGAEgASgOMiQuRW5jcnlwdG'
|
||||
'VkQ29udGVudC5NZXNzYWdlVXBkYXRlLlR5cGVSBHR5cGUSLQoPc2VuZGVyTWVzc2FnZUlkGAIg'
|
||||
'ASgJSABSD3NlbmRlck1lc3NhZ2VJZIgBARI6ChhtdWx0aXBsZVRhcmdldE1lc3NhZ2VJZHMYAy'
|
||||
'ADKAlSGG11bHRpcGxlVGFyZ2V0TWVzc2FnZUlkcxIXCgR0ZXh0GAQgASgJSAFSBHRleHSIAQES'
|
||||
'HAoJdGltZXN0YW1wGAUgASgDUgl0aW1lc3RhbXAiLQoEVHlwZRIKCgZERUxFVEUQABINCglFRE'
|
||||
'lUX1RFWFQQARIKCgZPUEVORUQQAkISChBfc2VuZGVyTWVzc2FnZUlkQgcKBV90ZXh0GowFCgVN'
|
||||
'ZWRpYRIoCg9zZW5kZXJNZXNzYWdlSWQYASABKAlSD3NlbmRlck1lc3NhZ2VJZBIwCgR0eXBlGA'
|
||||
'IgASgOMhwuRW5jcnlwdGVkQ29udGVudC5NZWRpYS5UeXBlUgR0eXBlEkMKGmRpc3BsYXlMaW1p'
|
||||
'dEluTWlsbGlzZWNvbmRzGAMgASgDSABSGmRpc3BsYXlMaW1pdEluTWlsbGlzZWNvbmRziAEBEj'
|
||||
'YKFnJlcXVpcmVzQXV0aGVudGljYXRpb24YBCABKAhSFnJlcXVpcmVzQXV0aGVudGljYXRpb24S'
|
||||
'HAoJdGltZXN0YW1wGAUgASgDUgl0aW1lc3RhbXASKwoOcXVvdGVNZXNzYWdlSWQYBiABKAlIAV'
|
||||
'IOcXVvdGVNZXNzYWdlSWSIAQESKQoNZG93bmxvYWRUb2tlbhgHIAEoDEgCUg1kb3dubG9hZFRv'
|
||||
'a2VuiAEBEikKDWVuY3J5cHRpb25LZXkYCCABKAxIA1INZW5jcnlwdGlvbktleYgBARIpCg1lbm'
|
||||
'NyeXB0aW9uTWFjGAkgASgMSARSDWVuY3J5cHRpb25NYWOIAQESLQoPZW5jcnlwdGlvbk5vbmNl'
|
||||
'GAogASgMSAVSD2VuY3J5cHRpb25Ob25jZYgBASIzCgRUeXBlEgwKCFJFVVBMT0FEEAASCQoFSU'
|
||||
'1BR0UQARIJCgVWSURFTxACEgcKA0dJRhADQh0KG19kaXNwbGF5TGltaXRJbk1pbGxpc2Vjb25k'
|
||||
'c0IRCg9fcXVvdGVNZXNzYWdlSWRCEAoOX2Rvd25sb2FkVG9rZW5CEAoOX2VuY3J5cHRpb25LZX'
|
||||
'lCEAoOX2VuY3J5cHRpb25NYWNCEgoQX2VuY3J5cHRpb25Ob25jZRqnAQoLTWVkaWFVcGRhdGUS'
|
||||
'NgoEdHlwZRgBIAEoDjIiLkVuY3J5cHRlZENvbnRlbnQuTWVkaWFVcGRhdGUuVHlwZVIEdHlwZR'
|
||||
'IoCg90YXJnZXRNZXNzYWdlSWQYAiABKAlSD3RhcmdldE1lc3NhZ2VJZCI2CgRUeXBlEgwKCFJF'
|
||||
'T1BFTkVEEAASCgoGU1RPUkVEEAESFAoQREVDUllQVElPTl9FUlJPUhACGngKDkNvbnRhY3RSZX'
|
||||
'F1ZXN0EjkKBHR5cGUYASABKA4yJS5FbmNyeXB0ZWRDb250ZW50LkNvbnRhY3RSZXF1ZXN0LlR5'
|
||||
'cGVSBHR5cGUiKwoEVHlwZRILCgdSRVFVRVNUEAASCgoGUkVKRUNUEAESCgoGQUNDRVBUEAIang'
|
||||
'IKDUNvbnRhY3RVcGRhdGUSOAoEdHlwZRgBIAEoDjIkLkVuY3J5cHRlZENvbnRlbnQuQ29udGFj'
|
||||
'dFVwZGF0ZS5UeXBlUgR0eXBlEjUKE2F2YXRhclN2Z0NvbXByZXNzZWQYAiABKAxIAFITYXZhdG'
|
||||
'FyU3ZnQ29tcHJlc3NlZIgBARIfCgh1c2VybmFtZRgDIAEoCUgBUgh1c2VybmFtZYgBARIlCgtk'
|
||||
'aXNwbGF5TmFtZRgEIAEoCUgCUgtkaXNwbGF5TmFtZYgBASIfCgRUeXBlEgsKB1JFUVVFU1QQAB'
|
||||
'IKCgZVUERBVEUQAUIWChRfYXZhdGFyU3ZnQ29tcHJlc3NlZEILCglfdXNlcm5hbWVCDgoMX2Rp'
|
||||
'c3BsYXlOYW1lGtUBCghQdXNoS2V5cxIzCgR0eXBlGAEgASgOMh8uRW5jcnlwdGVkQ29udGVudC'
|
||||
'5QdXNoS2V5cy5UeXBlUgR0eXBlEhkKBWtleUlkGAIgASgDSABSBWtleUlkiAEBEhUKA2tleRgD'
|
||||
'IAEoDEgBUgNrZXmIAQESIQoJY3JlYXRlZEF0GAQgASgDSAJSCWNyZWF0ZWRBdIgBASIfCgRUeX'
|
||||
'BlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAUIICgZfa2V5SWRCBgoEX2tleUIMCgpfY3JlYXRl'
|
||||
'ZEF0GocBCglGbGFtZVN5bmMSIgoMZmxhbWVDb3VudGVyGAEgASgDUgxmbGFtZUNvdW50ZXISNg'
|
||||
'oWbGFzdEZsYW1lQ291bnRlckNoYW5nZRgCIAEoA1IWbGFzdEZsYW1lQ291bnRlckNoYW5nZRIe'
|
||||
'CgpiZXN0RnJpZW5kGAMgASgIUgpiZXN0RnJpZW5kQgoKCF9ncm91cElkQg8KDV9pc0RpcmVjdE'
|
||||
'NoYXRCFwoVX3NlbmRlclByb2ZpbGVDb3VudGVyQhAKDl9tZXNzYWdlVXBkYXRlQggKBl9tZWRp'
|
||||
'YUIOCgxfbWVkaWFVcGRhdGVCEAoOX2NvbnRhY3RVcGRhdGVCEQoPX2NvbnRhY3RSZXF1ZXN0Qg'
|
||||
'wKCl9mbGFtZVN5bmNCCwoJX3B1c2hLZXlzQgsKCV9yZWFjdGlvbkIOCgxfdGV4dE1lc3NhZ2VC'
|
||||
'DgoMX2dyb3VwQ3JlYXRlQgwKCl9ncm91cEpvaW5CDgoMX2dyb3VwVXBkYXRlQhcKFV9yZXNlbm'
|
||||
'RHcm91cFB1YmxpY0tleQ==');
|
||||
'd0dyb3VwTmFtZRgDIAEoCUgBUgxuZXdHcm91cE5hbWWIAQESUwoibmV3RGVsZXRlTWVzc2FnZX'
|
||||
'NBZnRlck1pbGxpc2Vjb25kcxgEIAEoA0gCUiJuZXdEZWxldGVNZXNzYWdlc0FmdGVyTWlsbGlz'
|
||||
'ZWNvbmRziAEBQhQKEl9hZmZlY3RlZENvbnRhY3RJZEIPCg1fbmV3R3JvdXBOYW1lQiUKI19uZX'
|
||||
'dEZWxldGVNZXNzYWdlc0FmdGVyTWlsbGlzZWNvbmRzGqkBCgtUZXh0TWVzc2FnZRIoCg9zZW5k'
|
||||
'ZXJNZXNzYWdlSWQYASABKAlSD3NlbmRlck1lc3NhZ2VJZBISCgR0ZXh0GAIgASgJUgR0ZXh0Eh'
|
||||
'wKCXRpbWVzdGFtcBgDIAEoA1IJdGltZXN0YW1wEisKDnF1b3RlTWVzc2FnZUlkGAQgASgJSABS'
|
||||
'DnF1b3RlTWVzc2FnZUlkiAEBQhEKD19xdW90ZU1lc3NhZ2VJZBpiCghSZWFjdGlvbhIoCg90YX'
|
||||
'JnZXRNZXNzYWdlSWQYASABKAlSD3RhcmdldE1lc3NhZ2VJZBIUCgVlbW9qaRgCIAEoCVIFZW1v'
|
||||
'amkSFgoGcmVtb3ZlGAMgASgIUgZyZW1vdmUatwIKDU1lc3NhZ2VVcGRhdGUSOAoEdHlwZRgBIA'
|
||||
'EoDjIkLkVuY3J5cHRlZENvbnRlbnQuTWVzc2FnZVVwZGF0ZS5UeXBlUgR0eXBlEi0KD3NlbmRl'
|
||||
'ck1lc3NhZ2VJZBgCIAEoCUgAUg9zZW5kZXJNZXNzYWdlSWSIAQESOgoYbXVsdGlwbGVUYXJnZX'
|
||||
'RNZXNzYWdlSWRzGAMgAygJUhhtdWx0aXBsZVRhcmdldE1lc3NhZ2VJZHMSFwoEdGV4dBgEIAEo'
|
||||
'CUgBUgR0ZXh0iAEBEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1wIi0KBFR5cGUSCgoGRE'
|
||||
'VMRVRFEAASDQoJRURJVF9URVhUEAESCgoGT1BFTkVEEAJCEgoQX3NlbmRlck1lc3NhZ2VJZEIH'
|
||||
'CgVfdGV4dBqMBQoFTWVkaWESKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5kZXJNZXNzYW'
|
||||
'dlSWQSMAoEdHlwZRgCIAEoDjIcLkVuY3J5cHRlZENvbnRlbnQuTWVkaWEuVHlwZVIEdHlwZRJD'
|
||||
'ChpkaXNwbGF5TGltaXRJbk1pbGxpc2Vjb25kcxgDIAEoA0gAUhpkaXNwbGF5TGltaXRJbk1pbG'
|
||||
'xpc2Vjb25kc4gBARI2ChZyZXF1aXJlc0F1dGhlbnRpY2F0aW9uGAQgASgIUhZyZXF1aXJlc0F1'
|
||||
'dGhlbnRpY2F0aW9uEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1wEisKDnF1b3RlTWVzc2'
|
||||
'FnZUlkGAYgASgJSAFSDnF1b3RlTWVzc2FnZUlkiAEBEikKDWRvd25sb2FkVG9rZW4YByABKAxI'
|
||||
'AlINZG93bmxvYWRUb2tlbogBARIpCg1lbmNyeXB0aW9uS2V5GAggASgMSANSDWVuY3J5cHRpb2'
|
||||
'5LZXmIAQESKQoNZW5jcnlwdGlvbk1hYxgJIAEoDEgEUg1lbmNyeXB0aW9uTWFjiAEBEi0KD2Vu'
|
||||
'Y3J5cHRpb25Ob25jZRgKIAEoDEgFUg9lbmNyeXB0aW9uTm9uY2WIAQEiMwoEVHlwZRIMCghSRV'
|
||||
'VQTE9BRBAAEgkKBUlNQUdFEAESCQoFVklERU8QAhIHCgNHSUYQA0IdChtfZGlzcGxheUxpbWl0'
|
||||
'SW5NaWxsaXNlY29uZHNCEQoPX3F1b3RlTWVzc2FnZUlkQhAKDl9kb3dubG9hZFRva2VuQhAKDl'
|
||||
'9lbmNyeXB0aW9uS2V5QhAKDl9lbmNyeXB0aW9uTWFjQhIKEF9lbmNyeXB0aW9uTm9uY2UapwEK'
|
||||
'C01lZGlhVXBkYXRlEjYKBHR5cGUYASABKA4yIi5FbmNyeXB0ZWRDb250ZW50Lk1lZGlhVXBkYX'
|
||||
'RlLlR5cGVSBHR5cGUSKAoPdGFyZ2V0TWVzc2FnZUlkGAIgASgJUg90YXJnZXRNZXNzYWdlSWQi'
|
||||
'NgoEVHlwZRIMCghSRU9QRU5FRBAAEgoKBlNUT1JFRBABEhQKEERFQ1JZUFRJT05fRVJST1IQAh'
|
||||
'p4Cg5Db250YWN0UmVxdWVzdBI5CgR0eXBlGAEgASgOMiUuRW5jcnlwdGVkQ29udGVudC5Db250'
|
||||
'YWN0UmVxdWVzdC5UeXBlUgR0eXBlIisKBFR5cGUSCwoHUkVRVUVTVBAAEgoKBlJFSkVDVBABEg'
|
||||
'oKBkFDQ0VQVBACGp4CCg1Db250YWN0VXBkYXRlEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRD'
|
||||
'b250ZW50LkNvbnRhY3RVcGRhdGUuVHlwZVIEdHlwZRI1ChNhdmF0YXJTdmdDb21wcmVzc2VkGA'
|
||||
'IgASgMSABSE2F2YXRhclN2Z0NvbXByZXNzZWSIAQESHwoIdXNlcm5hbWUYAyABKAlIAVIIdXNl'
|
||||
'cm5hbWWIAQESJQoLZGlzcGxheU5hbWUYBCABKAlIAlILZGlzcGxheU5hbWWIAQEiHwoEVHlwZR'
|
||||
'ILCgdSRVFVRVNUEAASCgoGVVBEQVRFEAFCFgoUX2F2YXRhclN2Z0NvbXByZXNzZWRCCwoJX3Vz'
|
||||
'ZXJuYW1lQg4KDF9kaXNwbGF5TmFtZRrVAQoIUHVzaEtleXMSMwoEdHlwZRgBIAEoDjIfLkVuY3'
|
||||
'J5cHRlZENvbnRlbnQuUHVzaEtleXMuVHlwZVIEdHlwZRIZCgVrZXlJZBgCIAEoA0gAUgVrZXlJ'
|
||||
'ZIgBARIVCgNrZXkYAyABKAxIAVIDa2V5iAEBEiEKCWNyZWF0ZWRBdBgEIAEoA0gCUgljcmVhdG'
|
||||
'VkQXSIAQEiHwoEVHlwZRILCgdSRVFVRVNUEAASCgoGVVBEQVRFEAFCCAoGX2tleUlkQgYKBF9r'
|
||||
'ZXlCDAoKX2NyZWF0ZWRBdBqHAQoJRmxhbWVTeW5jEiIKDGZsYW1lQ291bnRlchgBIAEoA1IMZm'
|
||||
'xhbWVDb3VudGVyEjYKFmxhc3RGbGFtZUNvdW50ZXJDaGFuZ2UYAiABKANSFmxhc3RGbGFtZUNv'
|
||||
'dW50ZXJDaGFuZ2USHgoKYmVzdEZyaWVuZBgDIAEoCFIKYmVzdEZyaWVuZEIKCghfZ3JvdXBJZE'
|
||||
'IPCg1faXNEaXJlY3RDaGF0QhcKFV9zZW5kZXJQcm9maWxlQ291bnRlckIQCg5fbWVzc2FnZVVw'
|
||||
'ZGF0ZUIICgZfbWVkaWFCDgoMX21lZGlhVXBkYXRlQhAKDl9jb250YWN0VXBkYXRlQhEKD19jb2'
|
||||
'50YWN0UmVxdWVzdEIMCgpfZmxhbWVTeW5jQgsKCV9wdXNoS2V5c0ILCglfcmVhY3Rpb25CDgoM'
|
||||
'X3RleHRNZXNzYWdlQg4KDF9ncm91cENyZWF0ZUIMCgpfZ3JvdXBKb2luQg4KDF9ncm91cFVwZG'
|
||||
'F0ZUIXChVfcmVzZW5kR3JvdXBQdWJsaWNLZXk=');
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ message EncryptedContent {
|
|||
string groupActionType = 1; // GroupActionType.name
|
||||
optional int64 affectedContactId = 2;
|
||||
optional string newGroupName = 3;
|
||||
optional int64 newDeleteMessagesAfterMilliseconds = 4;
|
||||
}
|
||||
|
||||
message TextMessage {
|
||||
|
|
|
|||
|
|
@ -128,6 +128,16 @@ Future<void> handleGroupUpdate(
|
|||
contactId: Value(fromUserId),
|
||||
),
|
||||
);
|
||||
case GroupActionType.changeDisplayMaxTime:
|
||||
await twonlyDB.groupsDao.insertGroupAction(
|
||||
GroupHistoriesCompanion(
|
||||
groupId: Value(groupId),
|
||||
type: Value(actionType),
|
||||
newDeleteMessagesAfterMilliseconds:
|
||||
Value(update.newDeleteMessagesAfterMilliseconds.toInt()),
|
||||
contactId: Value(fromUserId),
|
||||
),
|
||||
);
|
||||
case GroupActionType.removedMember:
|
||||
case GroupActionType.addMember:
|
||||
case GroupActionType.leftGroup:
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ Future<void> enableTwonlySafe(String password) async {
|
|||
unawaited(performTwonlySafeBackup(force: true));
|
||||
}
|
||||
|
||||
Future<void> disableTwonlySafe() async {
|
||||
Future<void> removeTwonlySafeFromServer() async {
|
||||
final serverUrl = await getTwonlySafeBackupUrl();
|
||||
if (serverUrl != null) {
|
||||
try {
|
||||
|
|
@ -40,10 +40,6 @@ Future<void> disableTwonlySafe() async {
|
|||
Log.error('Could not connect to the server.');
|
||||
}
|
||||
}
|
||||
await updateUserdata((user) {
|
||||
user.twonlySafeBackup = null;
|
||||
return user;
|
||||
});
|
||||
}
|
||||
|
||||
Future<(Uint8List, Uint8List)> getMasterKey(
|
||||
|
|
|
|||
|
|
@ -26,61 +26,63 @@ class EmojiPickerBottom extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.all(30),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
color: Colors.grey,
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.all(30),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
color: Colors.grey,
|
||||
),
|
||||
height: 3,
|
||||
width: 60,
|
||||
),
|
||||
height: 3,
|
||||
width: 60,
|
||||
),
|
||||
Expanded(
|
||||
child: EmojiPicker(
|
||||
onEmojiSelected: (category, emoji) {
|
||||
Navigator.pop(
|
||||
context,
|
||||
EmojiLayerData(
|
||||
text: emoji.emoji,
|
||||
Expanded(
|
||||
child: EmojiPicker(
|
||||
onEmojiSelected: (category, emoji) {
|
||||
Navigator.pop(
|
||||
context,
|
||||
EmojiLayerData(
|
||||
text: emoji.emoji,
|
||||
),
|
||||
);
|
||||
},
|
||||
// textEditingController: _textFieldController,
|
||||
config: Config(
|
||||
height: 400,
|
||||
locale: Localizations.localeOf(context),
|
||||
viewOrderConfig: const ViewOrderConfig(
|
||||
top: EmojiPickerItem.searchBar,
|
||||
// middle: EmojiPickerItem.emojiView,
|
||||
bottom: EmojiPickerItem.categoryBar,
|
||||
),
|
||||
emojiTextStyle:
|
||||
TextStyle(fontSize: 24 * (Platform.isIOS ? 1.2 : 1)),
|
||||
emojiViewConfig: EmojiViewConfig(
|
||||
backgroundColor: context.color.surfaceContainer,
|
||||
),
|
||||
searchViewConfig: SearchViewConfig(
|
||||
backgroundColor: context.color.surfaceContainer,
|
||||
buttonIconColor: Colors.white,
|
||||
),
|
||||
categoryViewConfig: CategoryViewConfig(
|
||||
backgroundColor: context.color.surfaceContainer,
|
||||
dividerColor: Colors.white,
|
||||
indicatorColor: context.color.primary,
|
||||
iconColorSelected: context.color.primary,
|
||||
iconColor: context.color.secondary,
|
||||
),
|
||||
bottomActionBarConfig: BottomActionBarConfig(
|
||||
backgroundColor: context.color.surfaceContainer,
|
||||
buttonColor: context.color.surfaceContainer,
|
||||
buttonIconColor: context.color.secondary,
|
||||
),
|
||||
);
|
||||
},
|
||||
// textEditingController: _textFieldController,
|
||||
config: Config(
|
||||
height: 400,
|
||||
locale: Localizations.localeOf(context),
|
||||
viewOrderConfig: const ViewOrderConfig(
|
||||
top: EmojiPickerItem.searchBar,
|
||||
// middle: EmojiPickerItem.emojiView,
|
||||
bottom: EmojiPickerItem.categoryBar,
|
||||
),
|
||||
emojiTextStyle:
|
||||
TextStyle(fontSize: 24 * (Platform.isIOS ? 1.2 : 1)),
|
||||
emojiViewConfig: EmojiViewConfig(
|
||||
backgroundColor: context.color.surfaceContainer,
|
||||
),
|
||||
searchViewConfig: SearchViewConfig(
|
||||
backgroundColor: context.color.surfaceContainer,
|
||||
buttonIconColor: Colors.white,
|
||||
),
|
||||
categoryViewConfig: CategoryViewConfig(
|
||||
backgroundColor: context.color.surfaceContainer,
|
||||
dividerColor: Colors.white,
|
||||
indicatorColor: context.color.primary,
|
||||
iconColorSelected: context.color.primary,
|
||||
iconColor: context.color.secondary,
|
||||
),
|
||||
bottomActionBarConfig: BottomActionBarConfig(
|
||||
backgroundColor: context.color.surfaceContainer,
|
||||
buttonColor: context.color.surfaceContainer,
|
||||
buttonIconColor: context.color.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -114,7 +114,6 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
groupActionsSub?.cancel();
|
||||
lastOpenedMessageByContactSub?.cancel();
|
||||
tutorial?.cancel();
|
||||
textFieldFocus.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ class _AllReactionsViewState extends State<AllReactionsView> {
|
|||
),
|
||||
),
|
||||
);
|
||||
if (mounted) Navigator.pop(context);
|
||||
// if (mounted) Navigator.pop(context);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -55,6 +55,15 @@ class _ChatGroupActionState extends State<ChatGroupAction> {
|
|||
final maker = (contact == null) ? '' : getContactDisplayName(contact!);
|
||||
|
||||
switch (widget.action.type) {
|
||||
case GroupActionType.changeDisplayMaxTime:
|
||||
final time = formatDuration(
|
||||
context,
|
||||
(widget.action.newDeleteMessagesAfterMilliseconds ?? 0 / 1000) as int,
|
||||
);
|
||||
text = (contact == null)
|
||||
? context.lang.youChangedDisplayMaxTime(time)
|
||||
: context.lang.changeDisplayMaxTime(maker, time);
|
||||
icon = FontAwesomeIcons.pencil;
|
||||
case GroupActionType.updatedGroupName:
|
||||
text = (contact == null)
|
||||
? context.lang.youChangedGroupName(widget.action.newGroupName!)
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
bool imageSaved = false;
|
||||
bool imageSaving = false;
|
||||
bool displayTwonlyPresent = true;
|
||||
final emojiKey = GlobalKey<EmojiFloatWidgetState>();
|
||||
|
||||
StreamSubscription<MediaFile?>? downloadStateListener;
|
||||
|
||||
|
|
@ -634,6 +635,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
mediaViewerDistanceFromBottom: mediaViewerDistanceFromBottom,
|
||||
groupId: widget.group.groupId,
|
||||
messageId: currentMessage!.messageId,
|
||||
emojiKey: emojiKey,
|
||||
hide: () {
|
||||
setState(() {
|
||||
showShortReactions = false;
|
||||
|
|
@ -641,6 +643,9 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
});
|
||||
},
|
||||
),
|
||||
Positioned.fill(
|
||||
child: EmojiFloatWidget(key: emojiKey),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,11 +1,46 @@
|
|||
// ignore_for_file: avoid_dynamic_calls
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||
import 'package:twonly/src/services/api/messages.dart';
|
||||
import 'package:twonly/src/views/chats/media_viewer_components/reaction_buttons.component.dart';
|
||||
import 'package:twonly/src/views/components/animate_icon.dart';
|
||||
|
||||
Offset getGlobalOffset(GlobalKey targetKey) {
|
||||
final ctx = targetKey.currentContext;
|
||||
if (ctx == null) {
|
||||
return Offset.zero;
|
||||
}
|
||||
final renderObject = ctx.findRenderObject();
|
||||
if (renderObject is RenderBox) {
|
||||
return renderObject.localToGlobal(
|
||||
Offset(renderObject.size.width / 2, renderObject.size.height / 2),
|
||||
);
|
||||
}
|
||||
return Offset.zero;
|
||||
}
|
||||
|
||||
Future<void> sendReaction(
|
||||
String groupId,
|
||||
String messageId,
|
||||
String emoji,
|
||||
) async {
|
||||
await twonlyDB.reactionsDao.updateMyReaction(
|
||||
messageId,
|
||||
emoji,
|
||||
false,
|
||||
);
|
||||
await sendCipherTextToGroup(
|
||||
groupId,
|
||||
EncryptedContent(
|
||||
reaction: EncryptedContent_Reaction(
|
||||
targetMessageId: messageId,
|
||||
emoji: emoji,
|
||||
remove: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class EmojiReactionWidget extends StatefulWidget {
|
||||
const EmojiReactionWidget({
|
||||
required this.messageId,
|
||||
|
|
@ -13,74 +48,46 @@ class EmojiReactionWidget extends StatefulWidget {
|
|||
required this.hide,
|
||||
required this.show,
|
||||
required this.emoji,
|
||||
required this.emojiKey,
|
||||
super.key,
|
||||
});
|
||||
final String messageId;
|
||||
final String groupId;
|
||||
final Function hide;
|
||||
final void Function() hide;
|
||||
final bool show;
|
||||
final String emoji;
|
||||
final GlobalKey<EmojiFloatWidgetState> emojiKey;
|
||||
|
||||
@override
|
||||
State<EmojiReactionWidget> createState() => _EmojiReactionWidgetState();
|
||||
}
|
||||
|
||||
class _EmojiReactionWidgetState extends State<EmojiReactionWidget> {
|
||||
int selectedShortReaction = -1;
|
||||
final GlobalKey _targetKey = GlobalKey();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedSize(
|
||||
key: _targetKey,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.linearToEaseOut,
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
await twonlyDB.reactionsDao
|
||||
.updateMyReaction(widget.messageId, widget.emoji, false);
|
||||
|
||||
await sendCipherTextToGroup(
|
||||
widget.groupId,
|
||||
EncryptedContent(
|
||||
reaction: EncryptedContent_Reaction(
|
||||
targetMessageId: widget.messageId,
|
||||
emoji: widget.emoji,
|
||||
remove: false,
|
||||
),
|
||||
),
|
||||
await sendReaction(widget.groupId, widget.messageId, widget.emoji);
|
||||
widget.emojiKey.currentState?.spawn(
|
||||
getGlobalOffset(_targetKey),
|
||||
widget.emoji,
|
||||
);
|
||||
|
||||
setState(() {
|
||||
selectedShortReaction = 0; // Assuming index is 0 for this example
|
||||
});
|
||||
Future.delayed(const Duration(milliseconds: 300), () {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
widget.hide();
|
||||
selectedShortReaction = -1;
|
||||
});
|
||||
}
|
||||
});
|
||||
widget.hide();
|
||||
},
|
||||
child: (selectedShortReaction ==
|
||||
0) // Assuming index is 0 for this example
|
||||
? EmojiAnimationFlying(
|
||||
emoji: widget.emoji,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
startPosition: 0,
|
||||
size: (widget.show) ? 40 : 10,
|
||||
)
|
||||
: AnimatedOpacity(
|
||||
opacity: (selectedShortReaction == -1) ? 1 : 0, // Fade in/out
|
||||
duration: const Duration(milliseconds: 150),
|
||||
child: SizedBox(
|
||||
width: widget.show ? 40 : 10,
|
||||
child: Center(
|
||||
child: EmojiAnimation(
|
||||
emoji: widget.emoji,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: widget.show ? 40 : 10,
|
||||
child: Center(
|
||||
child: EmojiAnimation(
|
||||
emoji: widget.emoji,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/modules/all_emojis.dart';
|
||||
import 'package:twonly/src/views/chats/media_viewer_components/emoji_reactions_row.component.dart';
|
||||
import 'package:twonly/src/views/components/animate_icon.dart';
|
||||
|
||||
|
|
@ -11,6 +17,7 @@ class ReactionButtons extends StatefulWidget {
|
|||
required this.mediaViewerDistanceFromBottom,
|
||||
required this.messageId,
|
||||
required this.groupId,
|
||||
required this.emojiKey,
|
||||
required this.hide,
|
||||
super.key,
|
||||
});
|
||||
|
|
@ -18,6 +25,7 @@ class ReactionButtons extends StatefulWidget {
|
|||
final double mediaViewerDistanceFromBottom;
|
||||
final bool show;
|
||||
final bool textInputFocused;
|
||||
final GlobalKey<EmojiFloatWidgetState> emojiKey;
|
||||
final String messageId;
|
||||
final String groupId;
|
||||
final void Function() hide;
|
||||
|
|
@ -28,6 +36,7 @@ class ReactionButtons extends StatefulWidget {
|
|||
|
||||
class _ReactionButtonsState extends State<ReactionButtons> {
|
||||
int selectedShortReaction = -1;
|
||||
final GlobalKey _keyEmojiPicker = GlobalKey();
|
||||
|
||||
List<String> selectedEmojis =
|
||||
EmojiAnimation.animatedIcons.keys.toList().sublist(0, 6);
|
||||
|
|
@ -82,6 +91,7 @@ class _ReactionButtonsState extends State<ReactionButtons> {
|
|||
hide: widget.hide,
|
||||
show: widget.show,
|
||||
emoji: emoji as String,
|
||||
emojiKey: widget.emojiKey,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
|
|
@ -90,17 +100,53 @@ class _ReactionButtonsState extends State<ReactionButtons> {
|
|||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: firstRowEmojis
|
||||
.map(
|
||||
(emoji) => EmojiReactionWidget(
|
||||
messageId: widget.messageId,
|
||||
groupId: widget.groupId,
|
||||
hide: widget.hide,
|
||||
show: widget.show,
|
||||
emoji: emoji,
|
||||
children: [
|
||||
...firstRowEmojis.map(
|
||||
(emoji) => EmojiReactionWidget(
|
||||
messageId: widget.messageId,
|
||||
groupId: widget.groupId,
|
||||
hide: widget.hide,
|
||||
show: widget.show,
|
||||
emoji: emoji,
|
||||
emojiKey: widget.emojiKey,
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
key: _keyEmojiPicker,
|
||||
onTap: () async {
|
||||
// ignore: inference_failure_on_function_invocation
|
||||
final layer = await showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: context.color.surface,
|
||||
builder: (BuildContext context) {
|
||||
return const EmojiPickerBottom();
|
||||
},
|
||||
) as EmojiLayerData?;
|
||||
if (layer == null) return;
|
||||
await sendReaction(
|
||||
widget.groupId,
|
||||
widget.messageId,
|
||||
layer.text,
|
||||
);
|
||||
widget.emojiKey.currentState?.spawn(
|
||||
getGlobalOffset(_keyEmojiPicker),
|
||||
layer.text,
|
||||
);
|
||||
widget.hide();
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: context.color.surfaceContainer.withAlpha(100),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: const FaIcon(
|
||||
FontAwesomeIcons.ellipsisVertical,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -109,3 +155,164 @@ class _ReactionButtonsState extends State<ReactionButtons> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EmojiFloatWidget extends StatefulWidget {
|
||||
const EmojiFloatWidget({
|
||||
super.key,
|
||||
});
|
||||
@override
|
||||
EmojiFloatWidgetState createState() => EmojiFloatWidgetState();
|
||||
}
|
||||
|
||||
class EmojiFloatWidgetState extends State<EmojiFloatWidget>
|
||||
with SingleTickerProviderStateMixin {
|
||||
final List<_Particle> _particles = [];
|
||||
late final Ticker _ticker;
|
||||
final Random _rnd = Random();
|
||||
Duration _lastTick = Duration.zero;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_ticker = createTicker(_tick)..start();
|
||||
}
|
||||
|
||||
void _tick(Duration elapsed) {
|
||||
final dt = (_lastTick == Duration.zero)
|
||||
? 0.016
|
||||
: (elapsed - _lastTick).inMicroseconds / 1e6;
|
||||
_lastTick = elapsed;
|
||||
|
||||
for (final p in List<_Particle>.from(_particles)) {
|
||||
p.update(dt);
|
||||
if (p.isDead) _particles.remove(p);
|
||||
}
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_ticker.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// Call this to spawn the emoji animation from a global screen position.
|
||||
void spawn(Offset globalPosition, String emoji) {
|
||||
final box = context.findRenderObject() as RenderBox?;
|
||||
if (box == null) return;
|
||||
final local = box.globalToLocal(globalPosition);
|
||||
const spawnCount = 10;
|
||||
final life = const Duration(milliseconds: 2000).inMilliseconds / 1000.0;
|
||||
|
||||
for (var i = 0; i < spawnCount; i++) {
|
||||
final dx = (_rnd.nextDouble() - 0.5) * 220;
|
||||
final vx = dx;
|
||||
final vy = -(100 + _rnd.nextDouble() * 80);
|
||||
final rot = (_rnd.nextDouble() - 0.5) * 2;
|
||||
final scale = 0.9 + _rnd.nextDouble() * 0.6;
|
||||
|
||||
_particles.add(
|
||||
_Particle(
|
||||
emoji: emoji,
|
||||
x: local.dx,
|
||||
y: local.dy,
|
||||
vx: vx,
|
||||
vy: vy,
|
||||
rotation: rot,
|
||||
lifetime: life,
|
||||
scale: scale,
|
||||
),
|
||||
);
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IgnorePointer(
|
||||
child: CustomPaint(
|
||||
painter: _ParticlePainter(List<_Particle>.from(_particles)),
|
||||
size: Size.infinite,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Particle {
|
||||
_Particle({
|
||||
required this.emoji,
|
||||
required this.x,
|
||||
required this.y,
|
||||
required this.vx,
|
||||
required this.vy,
|
||||
required this.rotation,
|
||||
required this.lifetime,
|
||||
required this.scale,
|
||||
});
|
||||
final String emoji;
|
||||
double x;
|
||||
double y;
|
||||
double vx;
|
||||
double vy;
|
||||
double rotation;
|
||||
double age = 0;
|
||||
final double lifetime;
|
||||
final double scale;
|
||||
bool get isDead => age >= lifetime;
|
||||
|
||||
void update(double dt) {
|
||||
age += dt;
|
||||
// vertical-only motion emphasis: mild gravity slows ascent then gently pulls down
|
||||
vy += 100 * dt; // gravity (positive = down)
|
||||
// slight horizontal drag to reduce sideways drift
|
||||
vx *= 1 - 3.0 * dt;
|
||||
// integrate position
|
||||
x += vx * dt;
|
||||
y += vy * dt;
|
||||
// slow rotation decay
|
||||
rotation *= 1 - 1.5 * dt;
|
||||
}
|
||||
|
||||
double get progress => (age / lifetime).clamp(0.0, 1.0);
|
||||
|
||||
// opacity falls from 1 -> 0 as particle ages
|
||||
double get opacity => (1.0 - progress).clamp(0.0, 1.0);
|
||||
|
||||
// scale can gently grow then shrink; here we slightly increase early
|
||||
double get currentScale {
|
||||
final p = progress;
|
||||
if (p < 0.5) return scale * (1.0 + 0.3 * (p / 0.5));
|
||||
return scale * (1.3 - 0.3 * ((p - 0.5) / 0.5));
|
||||
}
|
||||
}
|
||||
|
||||
class _ParticlePainter extends CustomPainter {
|
||||
_ParticlePainter(this.particles);
|
||||
final List<_Particle> particles;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final textPainter = TextPainter(textDirection: TextDirection.ltr);
|
||||
for (final p in particles) {
|
||||
final tp = TextSpan(
|
||||
text: p.emoji,
|
||||
style: TextStyle(
|
||||
fontSize: 24 * p.currentScale,
|
||||
color: Colors.black.withValues(alpha: p.opacity),
|
||||
),
|
||||
);
|
||||
textPainter
|
||||
..text = tp
|
||||
..layout();
|
||||
canvas
|
||||
..save()
|
||||
..translate(p.x - textPainter.width / 2, p.y - textPainter.height / 2)
|
||||
..rotate(p.rotation);
|
||||
textPainter.paint(canvas, Offset.zero);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant _ParticlePainter old) => true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,10 +72,16 @@ class _AvatarIconState extends State<AvatarIcon> {
|
|||
_globalUserDataCallBackId = 'avatar_${getRandomString(10)}';
|
||||
globalUserDataChangedCallBack[_globalUserDataCallBackId!] = () {
|
||||
setState(() {
|
||||
_avatarSVGs = [gUser.avatarSvg!];
|
||||
if (gUser.avatarSvg != null) {
|
||||
_avatarSVGs = [gUser.avatarSvg!];
|
||||
} else {
|
||||
_avatarSVGs = [];
|
||||
}
|
||||
});
|
||||
};
|
||||
_avatarSVGs.add(gUser.avatarSvg!);
|
||||
if (gUser.avatarSvg != null) {
|
||||
_avatarSVGs = [gUser.avatarSvg!];
|
||||
}
|
||||
} else if (widget.contactId != null) {
|
||||
contactStream = twonlyDB.contactsDao
|
||||
.watchContact(widget.contactId!)
|
||||
|
|
|
|||
|
|
@ -82,13 +82,14 @@ class GroupContextMenu extends StatelessWidget {
|
|||
context.lang.groupContextMenuDeleteGroup,
|
||||
);
|
||||
if (ok) {
|
||||
await twonlyDB.messagesDao.deleteMessagesByGroupId(group.groupId);
|
||||
await twonlyDB.groupsDao.updateGroup(
|
||||
group.groupId,
|
||||
const GroupsCompanion(
|
||||
deletedContent: Value(true),
|
||||
),
|
||||
);
|
||||
// await twonlyDB.messagesDao.deleteMessagesByGroupId(group.groupId);
|
||||
await twonlyDB.groupsDao.deleteGroup(group.groupId);
|
||||
// await twonlyDB.groupsDao.updateGroup(
|
||||
// group.groupId,
|
||||
// const GroupsCompanion(
|
||||
// deletedContent: Value(true),
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
||||
import 'package:twonly/src/services/api/messages.dart';
|
||||
import 'package:twonly/src/services/twonly_safe/common.twonly_safe.dart';
|
||||
import 'package:twonly/src/services/twonly_safe/create_backup.twonly_safe.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
import 'package:twonly/src/views/components/better_list_title.dart';
|
||||
|
|
@ -59,6 +61,10 @@ class _ProfileViewState extends State<ProfileView> {
|
|||
return;
|
||||
}
|
||||
|
||||
// as the username has changes, remove the old from the server and then upload it again.
|
||||
await removeTwonlySafeFromServer();
|
||||
unawaited(performTwonlySafeBackup(force: true));
|
||||
|
||||
await updateUserdata((user) {
|
||||
user
|
||||
..username = username
|
||||
|
|
|
|||
Loading…
Reference in a new issue