mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-04-18 12:52:53 +00:00
Improve: Show message "Flames restored"
This commit is contained in:
parent
c85914672e
commit
c1065772f8
19 changed files with 192 additions and 15 deletions
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
- Fix: Issue with contact requests
|
||||
- Improve: Video compression with progress updates
|
||||
- Improve: Show message "Flames restored"
|
||||
|
||||
## 0.0.96
|
||||
|
||||
|
|
|
|||
1
assets/animated_icons/birthday-cake.json
Normal file
1
assets/animated_icons/birthday-cake.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -141,20 +141,20 @@ struct PushNotification: Sendable {
|
|||
var kind: PushKind = .reaction
|
||||
|
||||
var messageID: String {
|
||||
get {return _messageID ?? String()}
|
||||
get {_messageID ?? String()}
|
||||
set {_messageID = newValue}
|
||||
}
|
||||
/// Returns true if `messageID` has been explicitly set.
|
||||
var hasMessageID: Bool {return self._messageID != nil}
|
||||
var hasMessageID: Bool {self._messageID != nil}
|
||||
/// Clears the value of `messageID`. Subsequent reads from it will return its default value.
|
||||
mutating func clearMessageID() {self._messageID = nil}
|
||||
|
||||
var additionalContent: String {
|
||||
get {return _additionalContent ?? String()}
|
||||
get {_additionalContent ?? String()}
|
||||
set {_additionalContent = newValue}
|
||||
}
|
||||
/// Returns true if `additionalContent` has been explicitly set.
|
||||
var hasAdditionalContent: Bool {return self._additionalContent != nil}
|
||||
var hasAdditionalContent: Bool {self._additionalContent != nil}
|
||||
/// Clears the value of `additionalContent`. Subsequent reads from it will return its default value.
|
||||
mutating func clearAdditionalContent() {self._additionalContent = nil}
|
||||
|
||||
|
|
@ -190,11 +190,11 @@ struct PushUser: Sendable {
|
|||
var blocked: Bool = false
|
||||
|
||||
var lastMessageID: String {
|
||||
get {return _lastMessageID ?? String()}
|
||||
get {_lastMessageID ?? String()}
|
||||
set {_lastMessageID = newValue}
|
||||
}
|
||||
/// Returns true if `lastMessageID` has been explicitly set.
|
||||
var hasLastMessageID: Bool {return self._lastMessageID != nil}
|
||||
var hasLastMessageID: Bool {self._lastMessageID != nil}
|
||||
/// Clears the value of `lastMessageID`. Subsequent reads from it will return its default value.
|
||||
mutating func clearLastMessageID() {self._lastMessageID = nil}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import 'package:twonly/src/database/tables/contacts.table.dart';
|
|||
import 'package:twonly/src/database/tables/groups.table.dart';
|
||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||
|
||||
enum MessageType { media, text, contacts }
|
||||
enum MessageType { media, text, contacts, restoreFlameCounter }
|
||||
|
||||
@DataClassName('Message')
|
||||
class Messages extends Table {
|
||||
|
|
|
|||
|
|
@ -3033,6 +3033,12 @@ abstract class AppLocalizations {
|
|||
/// In en, this message translates to:
|
||||
/// **'Unknown contact whose identity has not yet been verified.'**
|
||||
String get verificationBadgeRedDesc;
|
||||
|
||||
/// No description provided for @chatEntryFlameRestored.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{count} flames restored'**
|
||||
String chatEntryFlameRestored(Object count);
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
|
|
|||
|
|
@ -1695,4 +1695,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get verificationBadgeRedDesc =>
|
||||
'Unbekannter Kontakt, dessen Identität bisher nicht verifiziert wurde.';
|
||||
|
||||
@override
|
||||
String chatEntryFlameRestored(Object count) {
|
||||
return '$count Flammen wiederhergestellt';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1683,4 +1683,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get verificationBadgeRedDesc =>
|
||||
'Unknown contact whose identity has not yet been verified.';
|
||||
|
||||
@override
|
||||
String chatEntryFlameRestored(Object count) {
|
||||
return '$count flames restored';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1683,4 +1683,9 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||
@override
|
||||
String get verificationBadgeRedDesc =>
|
||||
'Unknown contact whose identity has not yet been verified.';
|
||||
|
||||
@override
|
||||
String chatEntryFlameRestored(Object count) {
|
||||
return '$count flames restored';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,11 @@ message AdditionalMessageData {
|
|||
enum Type {
|
||||
LINK = 0;
|
||||
CONTACTS = 1;
|
||||
RESTORED_FLAME_COUNTER = 2;
|
||||
}
|
||||
Type type = 1;
|
||||
|
||||
optional string link = 2;
|
||||
repeated SharedContact contacts = 3;
|
||||
optional int64 restored_flame_counter = 4;
|
||||
}
|
||||
|
|
@ -106,11 +106,14 @@ class AdditionalMessageData extends $pb.GeneratedMessage {
|
|||
AdditionalMessageData_Type? type,
|
||||
$core.String? link,
|
||||
$core.Iterable<SharedContact>? contacts,
|
||||
$fixnum.Int64? restoredFlameCounter,
|
||||
}) {
|
||||
final result = create();
|
||||
if (type != null) result.type = type;
|
||||
if (link != null) result.link = link;
|
||||
if (contacts != null) result.contacts.addAll(contacts);
|
||||
if (restoredFlameCounter != null)
|
||||
result.restoredFlameCounter = restoredFlameCounter;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -135,6 +138,7 @@ class AdditionalMessageData extends $pb.GeneratedMessage {
|
|||
..pc<SharedContact>(
|
||||
3, _omitFieldNames ? '' : 'contacts', $pb.PbFieldType.PM,
|
||||
subBuilder: SharedContact.create)
|
||||
..aInt64(4, _omitFieldNames ? '' : 'restoredFlameCounter')
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
|
|
@ -180,6 +184,15 @@ class AdditionalMessageData extends $pb.GeneratedMessage {
|
|||
|
||||
@$pb.TagNumber(3)
|
||||
$pb.PbList<SharedContact> get contacts => $_getList(2);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$fixnum.Int64 get restoredFlameCounter => $_getI64(3);
|
||||
@$pb.TagNumber(4)
|
||||
set restoredFlameCounter($fixnum.Int64 value) => $_setInt64(3, value);
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasRestoredFlameCounter() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearRestoredFlameCounter() => $_clearField(4);
|
||||
}
|
||||
|
||||
const $core.bool _omitFieldNames =
|
||||
|
|
|
|||
|
|
@ -19,15 +19,19 @@ class AdditionalMessageData_Type extends $pb.ProtobufEnum {
|
|||
AdditionalMessageData_Type._(0, _omitEnumNames ? '' : 'LINK');
|
||||
static const AdditionalMessageData_Type CONTACTS =
|
||||
AdditionalMessageData_Type._(1, _omitEnumNames ? '' : 'CONTACTS');
|
||||
static const AdditionalMessageData_Type RESTORED_FLAME_COUNTER =
|
||||
AdditionalMessageData_Type._(
|
||||
2, _omitEnumNames ? '' : 'RESTORED_FLAME_COUNTER');
|
||||
|
||||
static const $core.List<AdditionalMessageData_Type> values =
|
||||
<AdditionalMessageData_Type>[
|
||||
LINK,
|
||||
CONTACTS,
|
||||
RESTORED_FLAME_COUNTER,
|
||||
];
|
||||
|
||||
static final $core.List<AdditionalMessageData_Type?> _byValue =
|
||||
$pb.ProtobufEnum.$_initByValueList(values, 1);
|
||||
$pb.ProtobufEnum.$_initByValueList(values, 2);
|
||||
static AdditionalMessageData_Type? valueOf($core.int value) =>
|
||||
value < 0 || value >= _byValue.length ? null : _byValue[value];
|
||||
|
||||
|
|
|
|||
|
|
@ -57,10 +57,20 @@ const AdditionalMessageData$json = {
|
|||
'6': '.SharedContact',
|
||||
'10': 'contacts'
|
||||
},
|
||||
{
|
||||
'1': 'restored_flame_counter',
|
||||
'3': 4,
|
||||
'4': 1,
|
||||
'5': 3,
|
||||
'9': 1,
|
||||
'10': 'restoredFlameCounter',
|
||||
'17': true
|
||||
},
|
||||
],
|
||||
'4': [AdditionalMessageData_Type$json],
|
||||
'8': [
|
||||
{'1': '_link'},
|
||||
{'1': '_restored_flame_counter'},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
@ -70,6 +80,7 @@ const AdditionalMessageData_Type$json = {
|
|||
'2': [
|
||||
{'1': 'LINK', '2': 0},
|
||||
{'1': 'CONTACTS', '2': 1},
|
||||
{'1': 'RESTORED_FLAME_COUNTER', '2': 2},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
@ -77,5 +88,7 @@ const AdditionalMessageData_Type$json = {
|
|||
final $typed_data.Uint8List additionalMessageDataDescriptor = $convert.base64Decode(
|
||||
'ChVBZGRpdGlvbmFsTWVzc2FnZURhdGESLwoEdHlwZRgBIAEoDjIbLkFkZGl0aW9uYWxNZXNzYW'
|
||||
'dlRGF0YS5UeXBlUgR0eXBlEhcKBGxpbmsYAiABKAlIAFIEbGlua4gBARIqCghjb250YWN0cxgD'
|
||||
'IAMoCzIOLlNoYXJlZENvbnRhY3RSCGNvbnRhY3RzIh4KBFR5cGUSCAoETElOSxAAEgwKCENPTl'
|
||||
'RBQ1RTEAFCBwoFX2xpbms=');
|
||||
'IAMoCzIOLlNoYXJlZENvbnRhY3RSCGNvbnRhY3RzEjkKFnJlc3RvcmVkX2ZsYW1lX2NvdW50ZX'
|
||||
'IYBCABKANIAVIUcmVzdG9yZWRGbGFtZUNvdW50ZXKIAQEiOgoEVHlwZRIICgRMSU5LEAASDAoI'
|
||||
'Q09OVEFDVFMQARIaChZSRVNUT1JFRF9GTEFNRV9DT1VOVEVSEAJCBwoFX2xpbmtCGQoXX3Jlc3'
|
||||
'RvcmVkX2ZsYW1lX2NvdW50ZXI=');
|
||||
|
|
|
|||
|
|
@ -303,6 +303,8 @@ Color getMessageColorFromType(
|
|||
Color color;
|
||||
|
||||
if (message.type == MessageType.text.name) {
|
||||
color = Colors.orange;
|
||||
} else if (message.type == MessageType.text.name) {
|
||||
color = Colors.blueAccent;
|
||||
} else if (mediaFile != null) {
|
||||
if (mediaFile.requiresAuthentication) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
|||
import 'package:twonly/src/views/chats/chat_messages_components/chat_reaction_row.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_audio_entry.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_contacts.entry.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_flame_restored.entry.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_media_entry.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_text_entry.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_unkown.entry.dart';
|
||||
|
|
@ -132,6 +133,12 @@ class _ChatListEntryState extends State<ChatListEntry> {
|
|||
);
|
||||
}
|
||||
|
||||
if (widget.message.type == MessageType.restoreFlameCounter.name) {
|
||||
return ChatFlameRestoredEntry(
|
||||
message: widget.message,
|
||||
);
|
||||
}
|
||||
|
||||
return const ChatUnknownEntry();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/model/protobuf/client/generated/data.pb.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/components/better_text.dart';
|
||||
|
||||
class ChatFlameRestoredEntry extends StatelessWidget {
|
||||
const ChatFlameRestoredEntry({
|
||||
required this.message,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Message message;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
AdditionalMessageData? data;
|
||||
|
||||
if (message.additionalMessageData != null) {
|
||||
try {
|
||||
data = AdditionalMessageData.fromBuffer(
|
||||
message.additionalMessageData!,
|
||||
);
|
||||
} catch (e) {
|
||||
data = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (data == null || !data.hasRestoredFlameCounter()) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Container(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * 0.8,
|
||||
),
|
||||
padding: const EdgeInsets.only(left: 10, top: 6, bottom: 6, right: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.orange,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: BetterText(
|
||||
text: context.lang
|
||||
.chatEntryFlameRestored(data.restoredFlameCounter.toInt()),
|
||||
textColor: isDarkMode(context) ? Colors.black : Colors.black,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -113,6 +113,7 @@ class EmojiAnimation extends StatelessWidget {
|
|||
'💯': '100.lottie',
|
||||
'🎉': 'party-popper.lottie',
|
||||
'🎊': 'confetti-ball.lottie',
|
||||
'🎂': 'birthday-cake.json',
|
||||
'🧡': 'orange-heart.lottie',
|
||||
'💛': 'yellow-heart.lottie',
|
||||
'💚': 'green-heart.lottie',
|
||||
|
|
|
|||
|
|
@ -82,10 +82,11 @@ class _FlameCounterWidgetState extends State<FlameCounterWidget> {
|
|||
if (widget.prefix) const SizedBox(width: 5),
|
||||
if (widget.prefix) const Text('•'),
|
||||
if (widget.prefix) const SizedBox(width: 5),
|
||||
Text(
|
||||
flameCounter.toString(),
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
if (flameCounter != 100)
|
||||
Text(
|
||||
flameCounter.toString(),
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(
|
||||
height: 15,
|
||||
child: EmojiAnimation(
|
||||
|
|
|
|||
|
|
@ -1,11 +1,18 @@
|
|||
import 'dart:async';
|
||||
import 'package:clock/clock.dart';
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/constants/routes.keys.dart';
|
||||
import 'package:twonly/src/database/tables/messages.table.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/model/protobuf/client/generated/data.pb.dart';
|
||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'
|
||||
as pb;
|
||||
import 'package:twonly/src/services/api/messages.dart';
|
||||
import 'package:twonly/src/services/flame.service.dart';
|
||||
import 'package:twonly/src/services/subscription.service.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
|
|
@ -46,7 +53,8 @@ class _MaxFlameListTitleState extends State<MaxFlameListTitle> {
|
|||
}
|
||||
|
||||
Future<void> _restoreFlames() async {
|
||||
if (!isUserAllowed(getCurrentPlan(), PremiumFeatures.RestoreFlames)) {
|
||||
if (!isUserAllowed(getCurrentPlan(), PremiumFeatures.RestoreFlames) &&
|
||||
kReleaseMode) {
|
||||
await context.push(Routes.settingsSubscription);
|
||||
return;
|
||||
}
|
||||
|
|
@ -60,7 +68,40 @@ class _MaxFlameListTitleState extends State<MaxFlameListTitle> {
|
|||
lastFlameCounterChange: Value(clock.now()),
|
||||
),
|
||||
);
|
||||
|
||||
final addData = AdditionalMessageData(
|
||||
type: AdditionalMessageData_Type.RESTORED_FLAME_COUNTER,
|
||||
restoredFlameCounter: Int64(_group!.maxFlameCounter),
|
||||
);
|
||||
|
||||
final message = await twonlyDB.messagesDao.insertMessage(
|
||||
MessagesCompanion(
|
||||
groupId: Value(_groupId),
|
||||
type: Value(MessageType.restoreFlameCounter.name),
|
||||
additionalMessageData: Value(addData.writeToBuffer()),
|
||||
),
|
||||
);
|
||||
|
||||
if (message == null) {
|
||||
Log.error('Could not insert message into database');
|
||||
return;
|
||||
}
|
||||
|
||||
final encryptedContent = pb.EncryptedContent(
|
||||
additionalDataMessage: pb.EncryptedContent_AdditionalDataMessage(
|
||||
senderMessageId: message.messageId,
|
||||
additionalMessageData: addData.writeToBuffer(),
|
||||
timestamp: Int64(message.createdAt.millisecondsSinceEpoch),
|
||||
type: MessageType.restoreFlameCounter.name,
|
||||
),
|
||||
);
|
||||
|
||||
await syncFlameCounters(forceForGroup: _groupId);
|
||||
await sendCipherTextToGroup(
|
||||
_groupId,
|
||||
encryptedContent,
|
||||
messageId: message.messageId,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import 'dart:async';
|
||||
import 'package:clock/clock.dart';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:restart_app/restart_app.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/constants/routes.keys.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||
|
||||
|
|
@ -67,6 +70,24 @@ class _DeveloperSettingsViewState extends State<DeveloperSettingsView> {
|
|||
}
|
||||
},
|
||||
),
|
||||
if (!kReleaseMode)
|
||||
ListTile(
|
||||
title: const Text('Make it possible to reset flames'),
|
||||
onTap: () async {
|
||||
final chats = await twonlyDB.groupsDao.getAllDirectChats();
|
||||
|
||||
for (final chat in chats) {
|
||||
await twonlyDB.groupsDao.updateGroup(
|
||||
chat.groupId,
|
||||
GroupsCompanion(
|
||||
flameCounter: const Value(0),
|
||||
maxFlameCounter: const Value(365),
|
||||
lastFlameCounterChange: Value(clock.now()),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
if (!kReleaseMode)
|
||||
ListTile(
|
||||
title: const Text('Automated Testing'),
|
||||
|
|
|
|||
Loading…
Reference in a new issue