Note to self feature addition #256
|
@ -1,3 +1,3 @@
|
|||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.enableJetifier=true
|
|
@ -135,6 +135,7 @@
|
|||
"conversations": {
|
||||
"speeddialNewChat": "New chat",
|
||||
"speeddialJoinGroupchat": "Join groupchat",
|
||||
"speeddialAddNoteToSelf": "Note to self",
|
||||
"overlaySettings": "Settings",
|
||||
"noOpenChats": "You have no open chats",
|
||||
"startChat": "Start a chat",
|
||||
|
|
|
@ -135,6 +135,7 @@
|
|||
"conversations": {
|
||||
"speeddialNewChat": "Neuer chat",
|
||||
"speeddialJoinGroupchat": "Gruppenchat beitreten",
|
||||
"speeddialAddNoteToSelf": "Notiz an mich",
|
||||
"overlaySettings": "Einstellungen",
|
||||
"noOpenChats": "Du hast keine offenen chats",
|
||||
"startChat": "Einen chat anfangen",
|
||||
|
|
|
@ -303,6 +303,7 @@ files:
|
|||
lastMessageBody: String
|
||||
avatarUrl: String
|
||||
jid: String
|
||||
conversationType: String
|
||||
- name: SetOpenConversationCommand
|
||||
extends: BackgroundCommand
|
||||
implements:
|
||||
|
|
|
@ -140,6 +140,7 @@ class ConversationService {
|
|||
Future<Conversation> addConversationFromData(
|
||||
String title,
|
||||
Message? lastMessage,
|
||||
ConversationType type,
|
||||
String avatarUrl,
|
||||
String jid,
|
||||
int unreadCounter,
|
||||
|
@ -156,6 +157,7 @@ class ConversationService {
|
|||
await GetIt.I.get<DatabaseService>().addConversationFromData(
|
||||
title,
|
||||
lastMessage,
|
||||
type,
|
||||
avatarUrl,
|
||||
jid,
|
||||
unreadCounter,
|
||||
|
|
|
@ -71,6 +71,7 @@ Future<void> createDatabase(Database db, int version) async {
|
|||
jid TEXT NOT NULL PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
avatarUrl TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
lastChangeTimestamp INTEGER NOT NULL,
|
||||
unreadCounter INTEGER NOT NULL,
|
||||
open INTEGER NOT NULL,
|
||||
|
|
|
@ -34,6 +34,7 @@ import 'package:moxxyv2/service/database/migrations/0000_stickers_privacy.dart';
|
|||
import 'package:moxxyv2/service/database/migrations/0000_xmpp_state.dart';
|
||||
import 'package:moxxyv2/service/database/migrations/0001_conversation_media_amount.dart';
|
||||
import 'package:moxxyv2/service/database/migrations/0001_conversation_primary_key.dart';
|
||||
import 'package:moxxyv2/service/database/migrations/0001_conversations_type.dart';
|
||||
import 'package:moxxyv2/service/database/migrations/0001_debug_menu.dart';
|
||||
import 'package:moxxyv2/service/database/migrations/0001_remove_auto_accept_subscriptions.dart';
|
||||
import 'package:moxxyv2/service/database/migrations/0001_subscriptions.dart';
|
||||
|
@ -121,7 +122,7 @@ class DatabaseService {
|
|||
_db = await openDatabase(
|
||||
dbPath,
|
||||
password: key,
|
||||
version: 30,
|
||||
version: 31,
|
||||
onCreate: createDatabase,
|
||||
onConfigure: (db) async {
|
||||
// In order to do schema changes during database upgrades, we disable foreign
|
||||
|
@ -250,6 +251,10 @@ class DatabaseService {
|
|||
_log.finest('Running migration for database version 30');
|
||||
await upgradeFromV29ToV30(db);
|
||||
}
|
||||
if (oldVersion < 31) {
|
||||
_log.finest('Running migration for database version 31');
|
||||
await upgradeFromV30ToV31(db);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -472,6 +477,7 @@ class DatabaseService {
|
|||
Future<Conversation> addConversationFromData(
|
||||
String title,
|
||||
Message? lastMessage,
|
||||
ConversationType type,
|
||||
String avatarUrl,
|
||||
String jid,
|
||||
int unreadCounter,
|
||||
|
@ -492,6 +498,7 @@ class DatabaseService {
|
|||
avatarUrl,
|
||||
jid,
|
||||
unreadCounter,
|
||||
type,
|
||||
lastChangeTimestamp,
|
||||
<SharedMedium>[],
|
||||
open,
|
||||
|
@ -576,6 +583,8 @@ class DatabaseService {
|
|||
String? stickerHashKey,
|
||||
int? pseudoMessageType,
|
||||
Map<String, dynamic>? pseudoMessageData,
|
||||
bool received = false,
|
||||
bool displayed = false,
|
||||
}) async {
|
||||
var m = Message(
|
||||
sender,
|
||||
|
@ -599,8 +608,8 @@ class DatabaseService {
|
|||
mediaWidth: mediaWidth,
|
||||
mediaHeight: mediaHeight,
|
||||
srcUrl: srcUrl,
|
||||
received: false,
|
||||
displayed: false,
|
||||
received: received,
|
||||
displayed: displayed,
|
||||
acked: false,
|
||||
originId: originId,
|
||||
filename: filename,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'package:moxxyv2/shared/models/conversation.dart';
|
||||
|
||||
/// Conversion helpers for bool <-> int as sqlite has no "real" booleans
|
||||
int boolToInt(bool b) => b ? 1 : 0;
|
||||
bool intToBool(int i) => i == 0 ? false : true;
|
||||
|
@ -7,3 +9,29 @@ bool stringToBool(String s) => s == 'true' ? true : false;
|
|||
|
||||
String intToString(int i) => '$i';
|
||||
int stringToInt(String s) => int.parse(s);
|
||||
|
||||
String conversationTypeToString(ConversationType type) {
|
||||
switch (type) {
|
||||
case ConversationType.chat:
|
||||
{
|
||||
return 'chat';
|
||||
}
|
||||
case ConversationType.note:
|
||||
{
|
||||
return 'note';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConversationType stringToConversationType(String type) {
|
||||
switch (type) {
|
||||
case 'chat':
|
||||
{
|
||||
return ConversationType.chat;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return ConversationType.note;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:moxxyv2/service/database/constants.dart';
|
||||
import 'package:moxxyv2/shared/models/conversation.dart';
|
||||
import 'package:sqflite_sqlcipher/sqflite.dart';
|
||||
|
||||
Future<void> upgradeFromV30ToV31(Database db) async {
|
||||
await db.execute(
|
||||
'ALTER TABLE $conversationsTable ADD COLUMN type TEXT NOT NULL DEFAULT "chat";',
|
||||
);
|
||||
}
|
|
@ -10,6 +10,7 @@ import 'package:moxxyv2/service/blocking.dart';
|
|||
import 'package:moxxyv2/service/contacts.dart';
|
||||
import 'package:moxxyv2/service/conversation.dart';
|
||||
import 'package:moxxyv2/service/database/database.dart';
|
||||
import 'package:moxxyv2/service/database/helpers.dart';
|
||||
import 'package:moxxyv2/service/helpers.dart';
|
||||
import 'package:moxxyv2/service/httpfiletransfer/helpers.dart';
|
||||
import 'package:moxxyv2/service/httpfiletransfer/httpfiletransfer.dart';
|
||||
|
@ -30,6 +31,7 @@ import 'package:moxxyv2/shared/commands.dart';
|
|||
import 'package:moxxyv2/shared/eventhandler.dart';
|
||||
import 'package:moxxyv2/shared/events.dart';
|
||||
import 'package:moxxyv2/shared/helpers.dart';
|
||||
import 'package:moxxyv2/shared/models/conversation.dart';
|
||||
import 'package:moxxyv2/shared/models/preferences.dart';
|
||||
import 'package:moxxyv2/shared/models/reaction.dart';
|
||||
import 'package:moxxyv2/shared/models/sticker.dart' as sticker;
|
||||
|
@ -233,6 +235,7 @@ Future<void> performAddConversation(
|
|||
final newConversation = await cs.addConversationFromData(
|
||||
command.title,
|
||||
null,
|
||||
stringToConversationType(command.conversationType),
|
||||
command.avatarUrl,
|
||||
command.jid,
|
||||
0,
|
||||
|
@ -289,7 +292,8 @@ Future<void> performSetOpenConversation(
|
|||
await GetIt.I.get<XmppService>().setCurrentlyOpenedChatJid(command.jid ?? '');
|
||||
|
||||
// Null just means that the chat has been closed
|
||||
if (command.jid != null) {
|
||||
// Empty string JID for notes to self
|
||||
if (command.jid != null && command.jid != '') {
|
||||
await GetIt.I
|
||||
.get<NotificationsService>()
|
||||
.dismissNotificationsByJid(command.jid!);
|
||||
|
@ -462,6 +466,7 @@ Future<void> performAddContact(
|
|||
final newConversation = await cs.addConversationFromData(
|
||||
jid.split('@')[0],
|
||||
null,
|
||||
ConversationType.chat,
|
||||
'',
|
||||
jid,
|
||||
0,
|
||||
|
@ -654,9 +659,12 @@ Future<void> performSendChatState(
|
|||
if (!prefs.sendChatMarkers) return;
|
||||
|
||||
final conn = GetIt.I.get<XmppConnection>();
|
||||
conn
|
||||
.getManagerById<ChatStateManager>(chatStateManager)!
|
||||
.sendChatState(chatStateFromString(command.state), command.jid);
|
||||
|
||||
if (command.jid != '') {
|
||||
conn
|
||||
.getManagerById<ChatStateManager>(chatStateManager)!
|
||||
.sendChatState(chatStateFromString(command.state), command.jid);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> performGetFeatures(
|
||||
|
@ -847,19 +855,20 @@ Future<void> performMessageRetraction(
|
|||
'',
|
||||
true,
|
||||
);
|
||||
|
||||
// Send the retraction
|
||||
(GetIt.I.get<XmppConnection>().getManagerById(messageManager)!
|
||||
as MessageManager)
|
||||
.sendMessage(
|
||||
MessageDetails(
|
||||
to: command.conversationJid,
|
||||
messageRetraction: MessageRetractionData(
|
||||
command.originId,
|
||||
t.messages.retractedFallback,
|
||||
if (command.conversationJid != '') {
|
||||
(GetIt.I
|
||||
.get<XmppConnection>()
|
||||
.getManagerById<MessageManager>(messageManager)!)
|
||||
.sendMessage(
|
||||
MessageDetails(
|
||||
to: command.conversationJid,
|
||||
messageRetraction: MessageRetractionData(
|
||||
command.originId,
|
||||
t.messages.retractedFallback,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> performMarkConversationAsRead(
|
||||
|
@ -945,19 +954,21 @@ Future<void> performAddMessageReaction(
|
|||
final ownReactions =
|
||||
reactions.where((r) => r.reactedBySelf).map((r) => r.emoji).toList();
|
||||
|
||||
// Send the reaction
|
||||
conn.getManagerById<MessageManager>(messageManager)!.sendMessage(
|
||||
MessageDetails(
|
||||
to: command.conversationJid,
|
||||
messageReactions: MessageReactions(
|
||||
msg.originId ?? msg.sid,
|
||||
ownReactions,
|
||||
if (command.conversationJid != '') {
|
||||
// Send the reaction
|
||||
conn.getManagerById<MessageManager>(messageManager)!.sendMessage(
|
||||
MessageDetails(
|
||||
to: command.conversationJid,
|
||||
messageReactions: MessageReactions(
|
||||
msg.originId ?? msg.sid,
|
||||
ownReactions,
|
||||
),
|
||||
requestChatMarkers: false,
|
||||
messageProcessingHints:
|
||||
!msg.containsNoStore ? [MessageProcessingHint.store] : null,
|
||||
),
|
||||
requestChatMarkers: false,
|
||||
messageProcessingHints:
|
||||
!msg.containsNoStore ? [MessageProcessingHint.store] : null,
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> performRemoveMessageReaction(
|
||||
|
@ -985,19 +996,21 @@ Future<void> performRemoveMessageReaction(
|
|||
final ownReactions =
|
||||
reactions.where((r) => r.reactedBySelf).map((r) => r.emoji).toList();
|
||||
|
||||
// Send the reaction
|
||||
conn.getManagerById<MessageManager>(messageManager)!.sendMessage(
|
||||
MessageDetails(
|
||||
to: command.conversationJid,
|
||||
messageReactions: MessageReactions(
|
||||
msg.originId ?? msg.sid,
|
||||
ownReactions,
|
||||
if (command.conversationJid != '') {
|
||||
// Send the reaction
|
||||
conn.getManagerById<MessageManager>(messageManager)!.sendMessage(
|
||||
MessageDetails(
|
||||
to: command.conversationJid,
|
||||
messageReactions: MessageReactions(
|
||||
msg.originId ?? msg.sid,
|
||||
ownReactions,
|
||||
),
|
||||
requestChatMarkers: false,
|
||||
messageProcessingHints:
|
||||
!msg.containsNoStore ? [MessageProcessingHint.store] : null,
|
||||
),
|
||||
requestChatMarkers: false,
|
||||
messageProcessingHints:
|
||||
!msg.containsNoStore ? [MessageProcessingHint.store] : null,
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> performMarkDeviceVerified(
|
||||
|
|
|
@ -91,6 +91,8 @@ class MessageService {
|
|||
String? stickerHashKey,
|
||||
int? pseudoMessageType,
|
||||
Map<String, dynamic>? pseudoMessageData,
|
||||
bool received = false,
|
||||
bool displayed = false,
|
||||
}) async {
|
||||
final msg = await GetIt.I.get<DatabaseService>().addMessageFromData(
|
||||
body,
|
||||
|
@ -125,6 +127,8 @@ class MessageService {
|
|||
stickerHashKey: stickerHashKey,
|
||||
pseudoMessageType: pseudoMessageType,
|
||||
pseudoMessageData: pseudoMessageData,
|
||||
received: received,
|
||||
displayed: displayed,
|
||||
);
|
||||
|
||||
await _cacheLock.synchronized(() {
|
||||
|
|
|
@ -34,6 +34,7 @@ import 'package:moxxyv2/shared/error_types.dart';
|
|||
import 'package:moxxyv2/shared/eventhandler.dart';
|
||||
import 'package:moxxyv2/shared/events.dart';
|
||||
import 'package:moxxyv2/shared/helpers.dart';
|
||||
import 'package:moxxyv2/shared/models/conversation.dart';
|
||||
import 'package:moxxyv2/shared/models/media.dart';
|
||||
import 'package:moxxyv2/shared/models/message.dart';
|
||||
import 'package:moxxyv2/shared/models/reaction.dart';
|
||||
|
@ -182,15 +183,17 @@ class XmppService {
|
|||
sendEvent(ConversationUpdatedEvent(conversation: conversation));
|
||||
}
|
||||
|
||||
// Send the correction
|
||||
conn.getManagerById<MessageManager>(messageManager)!.sendMessage(
|
||||
MessageDetails(
|
||||
to: recipient,
|
||||
body: newBody,
|
||||
lastMessageCorrectionId: oldId,
|
||||
chatState: chatState,
|
||||
),
|
||||
);
|
||||
if (conversation?.type != ConversationType.note) {
|
||||
// Send the correction
|
||||
conn.getManagerById<MessageManager>(messageManager)!.sendMessage(
|
||||
MessageDetails(
|
||||
to: recipient,
|
||||
body: newBody,
|
||||
lastMessageCorrectionId: oldId,
|
||||
chatState: chatState,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a message to JIDs in [recipients] with the body of [body].
|
||||
|
@ -224,7 +227,7 @@ class XmppService {
|
|||
sticker != null,
|
||||
sid,
|
||||
false,
|
||||
c.encrypted,
|
||||
c.type == ConversationType.note ? true : c.encrypted,
|
||||
// TODO(Unknown): Maybe make this depend on some setting
|
||||
false,
|
||||
originId: originId,
|
||||
|
@ -233,6 +236,8 @@ class XmppService {
|
|||
stickerHashKey: sticker?.hashKey,
|
||||
srcUrl: sticker?.urlSources.first,
|
||||
mediaType: sticker?.mediaType,
|
||||
received: c.type == ConversationType.note ? true : false,
|
||||
displayed: c.type == ConversationType.note ? true : false,
|
||||
);
|
||||
|
||||
final newConversation = await cs.updateConversation(
|
||||
|
@ -256,42 +261,44 @@ class XmppService {
|
|||
);
|
||||
}
|
||||
|
||||
conn.getManagerById<MessageManager>(messageManager)!.sendMessage(
|
||||
MessageDetails(
|
||||
to: recipient,
|
||||
body: body,
|
||||
requestDeliveryReceipt: true,
|
||||
id: sid,
|
||||
originId: originId,
|
||||
quoteBody: createFallbackBodyForQuotedMessage(quotedMessage),
|
||||
quoteFrom: quotedMessage?.sender,
|
||||
quoteId: quotedMessage?.sid,
|
||||
chatState: chatState,
|
||||
shouldEncrypt: conversation!.encrypted,
|
||||
stickerPackId: sticker?.stickerPackId,
|
||||
sfs: sticker == null
|
||||
? null
|
||||
: StatelessFileSharingData(
|
||||
FileMetadataData(
|
||||
mediaType: sticker.mediaType,
|
||||
width: sticker.width,
|
||||
height: sticker.height,
|
||||
desc: sticker.desc,
|
||||
size: sticker.size,
|
||||
thumbnails: [],
|
||||
hashes: sticker.hashes,
|
||||
if (conversation?.type == ConversationType.chat) {
|
||||
conn.getManagerById<MessageManager>(messageManager)!.sendMessage(
|
||||
MessageDetails(
|
||||
to: recipient,
|
||||
body: body,
|
||||
requestDeliveryReceipt: true,
|
||||
id: sid,
|
||||
originId: originId,
|
||||
quoteBody: createFallbackBodyForQuotedMessage(quotedMessage),
|
||||
quoteFrom: quotedMessage?.sender,
|
||||
quoteId: quotedMessage?.sid,
|
||||
chatState: chatState,
|
||||
shouldEncrypt: conversation!.encrypted,
|
||||
stickerPackId: sticker?.stickerPackId,
|
||||
sfs: sticker == null
|
||||
? null
|
||||
: StatelessFileSharingData(
|
||||
FileMetadataData(
|
||||
mediaType: sticker.mediaType,
|
||||
width: sticker.width,
|
||||
height: sticker.height,
|
||||
desc: sticker.desc,
|
||||
size: sticker.size,
|
||||
thumbnails: [],
|
||||
hashes: sticker.hashes,
|
||||
),
|
||||
sticker.urlSources
|
||||
// ignore: unnecessary_lambdas
|
||||
.map((s) => StatelessFileSharingUrlSource(s))
|
||||
.toList(),
|
||||
),
|
||||
sticker.urlSources
|
||||
// ignore: unnecessary_lambdas
|
||||
.map((s) => StatelessFileSharingUrlSource(s))
|
||||
.toList(),
|
||||
),
|
||||
setOOBFallbackBody: sticker != null ? false : true,
|
||||
),
|
||||
);
|
||||
setOOBFallbackBody: sticker != null ? false : true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
sendEvent(
|
||||
ConversationUpdatedEvent(conversation: conversation),
|
||||
ConversationUpdatedEvent(conversation: conversation!),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -537,7 +544,9 @@ class XmppService {
|
|||
true,
|
||||
conn.generateId(),
|
||||
false,
|
||||
encrypt[recipient]!,
|
||||
conversation?.type == ConversationType.note
|
||||
? true
|
||||
: encrypt[recipient]!,
|
||||
// TODO(Unknown): Maybe make this depend on some setting
|
||||
false,
|
||||
mediaUrl: path,
|
||||
|
@ -546,7 +555,10 @@ class XmppService {
|
|||
mediaWidth: dimensions[path]?.width.toInt(),
|
||||
mediaHeight: dimensions[path]?.height.toInt(),
|
||||
filename: pathlib.basename(path),
|
||||
isUploading: true,
|
||||
isUploading:
|
||||
conversation?.type != ConversationType.note ? true : false,
|
||||
received: conversation?.type == ConversationType.note ? true : false,
|
||||
displayed: conversation?.type == ConversationType.note ? true : false,
|
||||
);
|
||||
if (messages.containsKey(path)) {
|
||||
messages[path]![recipient] = msg;
|
||||
|
@ -578,6 +590,7 @@ class XmppService {
|
|||
// TODO(Unknown): Should we use the JID parser?
|
||||
rosterItem?.title ?? recipient.split('@').first,
|
||||
lastMessages[recipient],
|
||||
ConversationType.chat,
|
||||
rosterItem?.avatarUrl ?? '',
|
||||
recipient,
|
||||
0,
|
||||
|
@ -665,37 +678,40 @@ class XmppService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send an upload notification
|
||||
conn.getManagerById<MessageManager>(messageManager)!.sendMessage(
|
||||
MessageDetails(
|
||||
to: recipient,
|
||||
id: messages[path]![recipient]!.sid,
|
||||
fun: FileMetadataData(
|
||||
// TODO(Unknown): Maybe add media type specific metadata
|
||||
mediaType: lookupMimeType(path),
|
||||
name: pathlib.basename(path),
|
||||
size: File(path).statSync().size,
|
||||
thumbnails: thumbnails[path] ?? [],
|
||||
if (recipient != '') {
|
||||
conn.getManagerById<MessageManager>(messageManager)!.sendMessage(
|
||||
MessageDetails(
|
||||
to: recipient,
|
||||
id: messages[path]![recipient]!.sid,
|
||||
fun: FileMetadataData(
|
||||
// TODO(Unknown): Maybe add media type specific metadata
|
||||
mediaType: lookupMimeType(path),
|
||||
name: pathlib.basename(path),
|
||||
size: File(path).statSync().size,
|
||||
thumbnails: thumbnails[path] ?? [],
|
||||
),
|
||||
shouldEncrypt: encrypt[recipient]!,
|
||||
),
|
||||
shouldEncrypt: encrypt[recipient]!,
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await hfts.uploadFile(
|
||||
FileUploadJob(
|
||||
recipients,
|
||||
path,
|
||||
pathMime,
|
||||
encrypt,
|
||||
messages[path]!,
|
||||
thumbnails[path] ?? [],
|
||||
),
|
||||
);
|
||||
}
|
||||
recipients.remove('');
|
||||
|
||||
_log.finest('File upload submitted');
|
||||
if (recipients.isNotEmpty) {
|
||||
await hfts.uploadFile(
|
||||
FileUploadJob(
|
||||
recipients,
|
||||
path,
|
||||
pathMime,
|
||||
encrypt,
|
||||
messages[path]!,
|
||||
thumbnails[path] ?? [],
|
||||
),
|
||||
);
|
||||
_log.finest('File upload submitted');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _initializeOmemoService(String jid) async {
|
||||
|
@ -1420,6 +1436,7 @@ class XmppService {
|
|||
final newConversation = await cs.addConversationFromData(
|
||||
rosterItem?.title ?? conversationJid.split('@')[0],
|
||||
message,
|
||||
ConversationType.chat,
|
||||
rosterItem?.avatarUrl ?? '',
|
||||
conversationJid,
|
||||
sent ? 0 : 1,
|
||||
|
|
|
@ -40,6 +40,13 @@ class ConversationMessageConverter
|
|||
};
|
||||
}
|
||||
|
||||
enum ConversationType {
|
||||
@JsonValue('chat')
|
||||
chat,
|
||||
@JsonValue('note')
|
||||
note
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Conversation with _$Conversation {
|
||||
factory Conversation(
|
||||
|
@ -48,6 +55,7 @@ class Conversation with _$Conversation {
|
|||
String avatarUrl,
|
||||
String jid,
|
||||
int unreadCounter,
|
||||
ConversationType type,
|
||||
// NOTE: In milliseconds since Epoch or -1 if none has ever happened
|
||||
int lastChangeTimestamp,
|
||||
List<SharedMedium> sharedMedia,
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
|||
import 'package:get_it/get_it.dart';
|
||||
import 'package:moxlib/moxlib.dart';
|
||||
import 'package:moxplatform/moxplatform.dart';
|
||||
import 'package:moxxyv2/service/database/helpers.dart';
|
||||
import 'package:moxxyv2/shared/commands.dart';
|
||||
import 'package:moxxyv2/shared/events.dart';
|
||||
import 'package:moxxyv2/shared/models/conversation.dart';
|
||||
|
@ -63,6 +64,7 @@ class NewConversationBloc
|
|||
jid: event.jid,
|
||||
avatarUrl: event.avatarUrl,
|
||||
lastMessageBody: '',
|
||||
conversationType: conversationTypeToString(event.type),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
@ -11,10 +11,11 @@ class NewConversationInitEvent extends NewConversationEvent {
|
|||
|
||||
/// Triggered when a new conversation has been added by the UI
|
||||
class NewConversationAddedEvent extends NewConversationEvent {
|
||||
NewConversationAddedEvent(this.jid, this.title, this.avatarUrl);
|
||||
NewConversationAddedEvent(this.jid, this.title, this.avatarUrl, this.type);
|
||||
final String jid;
|
||||
final String title;
|
||||
final String avatarUrl;
|
||||
final ConversationType type;
|
||||
}
|
||||
|
||||
/// Triggered when a roster item has been removed by the UI
|
||||
|
|
|
@ -10,6 +10,7 @@ import 'package:grouped_list/grouped_list.dart';
|
|||
import 'package:moxxyv2/i18n/strings.g.dart';
|
||||
import 'package:moxxyv2/shared/error_types.dart';
|
||||
import 'package:moxxyv2/shared/helpers.dart';
|
||||
import 'package:moxxyv2/shared/models/conversation.dart';
|
||||
import 'package:moxxyv2/shared/models/message.dart';
|
||||
import 'package:moxxyv2/shared/warning_types.dart';
|
||||
import 'package:moxxyv2/ui/bloc/conversation_bloc.dart';
|
||||
|
@ -322,7 +323,7 @@ class ConversationPageState extends State<ConversationPage>
|
|||
},
|
||||
),
|
||||
|
||||
if (item.isQuotable)
|
||||
if (item.isQuotable && item.conversationJid != '')
|
||||
OverviewMenuItem(
|
||||
icon: Icons.forward,
|
||||
text: t.pages.conversation.forward,
|
||||
|
@ -488,7 +489,8 @@ class ConversationPageState extends State<ConversationPage>
|
|||
prev.conversation?.inRoster !=
|
||||
next.conversation?.inRoster,
|
||||
builder: (context, state) {
|
||||
if (state.conversation?.inRoster ?? false) {
|
||||
if ((state.conversation?.inRoster ?? false) ||
|
||||
state.conversation?.type == ConversationType.note) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:moxxmpp/moxxmpp.dart';
|
||||
import 'package:moxxyv2/i18n/strings.g.dart';
|
||||
import 'package:moxxyv2/shared/models/conversation.dart';
|
||||
import 'package:moxxyv2/ui/bloc/conversation_bloc.dart';
|
||||
import 'package:moxxyv2/ui/bloc/conversations_bloc.dart';
|
||||
import 'package:moxxyv2/ui/bloc/navigation_bloc.dart';
|
||||
|
@ -171,37 +172,38 @@ class ConversationTopbar extends StatelessWidget
|
|||
),
|
||||
),
|
||||
),
|
||||
// ignore: implicit_dynamic_type
|
||||
PopupMenuButton(
|
||||
onSelected: (result) {
|
||||
if (result == EncryptionOption.omemo &&
|
||||
state.conversation!.encrypted == false) {
|
||||
context
|
||||
.read<ConversationBloc>()
|
||||
.add(OmemoSetEvent(true));
|
||||
} else if (result == EncryptionOption.none &&
|
||||
state.conversation!.encrypted == true) {
|
||||
context
|
||||
.read<ConversationBloc>()
|
||||
.add(OmemoSetEvent(false));
|
||||
}
|
||||
},
|
||||
icon: (state.conversation?.encrypted ?? false)
|
||||
? const Icon(Icons.lock)
|
||||
: const Icon(Icons.lock_open),
|
||||
itemBuilder: (BuildContext c) => [
|
||||
popupItemWithIcon(
|
||||
EncryptionOption.none,
|
||||
t.pages.conversation.unencrypted,
|
||||
Icons.lock_open,
|
||||
),
|
||||
popupItemWithIcon(
|
||||
EncryptionOption.omemo,
|
||||
t.pages.conversation.encrypted,
|
||||
Icons.lock,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (state.conversation?.type != ConversationType.note)
|
||||
// ignore: implicit_dynamic_type
|
||||
PopupMenuButton(
|
||||
onSelected: (result) {
|
||||
if (result == EncryptionOption.omemo &&
|
||||
state.conversation!.encrypted == false) {
|
||||
context
|
||||
.read<ConversationBloc>()
|
||||
.add(OmemoSetEvent(true));
|
||||
} else if (result == EncryptionOption.none &&
|
||||
state.conversation!.encrypted == true) {
|
||||
context
|
||||
.read<ConversationBloc>()
|
||||
.add(OmemoSetEvent(false));
|
||||
}
|
||||
},
|
||||
icon: (state.conversation?.encrypted ?? false)
|
||||
? const Icon(Icons.lock)
|
||||
: const Icon(Icons.lock_open),
|
||||
itemBuilder: (BuildContext c) => [
|
||||
popupItemWithIcon(
|
||||
EncryptionOption.none,
|
||||
t.pages.conversation.unencrypted,
|
||||
Icons.lock_open,
|
||||
),
|
||||
popupItemWithIcon(
|
||||
EncryptionOption.omemo,
|
||||
t.pages.conversation.encrypted,
|
||||
Icons.lock,
|
||||
),
|
||||
],
|
||||
),
|
||||
// ignore: implicit_dynamic_type
|
||||
PopupMenuButton(
|
||||
onSelected: (result) async {
|
||||
|
@ -244,11 +246,12 @@ class ConversationTopbar extends StatelessWidget
|
|||
t.pages.conversation.closeChat,
|
||||
Icons.close,
|
||||
),
|
||||
popupItemWithIcon(
|
||||
ConversationOption.block,
|
||||
t.pages.conversation.blockUser,
|
||||
Icons.block,
|
||||
)
|
||||
if (state.conversation?.type != ConversationType.note)
|
||||
popupItemWithIcon(
|
||||
ConversationOption.block,
|
||||
t.pages.conversation.blockUser,
|
||||
Icons.block,
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:moxxyv2/i18n/strings.g.dart';
|
|||
import 'package:moxxyv2/shared/models/conversation.dart';
|
||||
import 'package:moxxyv2/ui/bloc/conversation_bloc.dart';
|
||||
import 'package:moxxyv2/ui/bloc/conversations_bloc.dart';
|
||||
import 'package:moxxyv2/ui/bloc/newconversation_bloc.dart';
|
||||
import 'package:moxxyv2/ui/bloc/profile_bloc.dart' as profile;
|
||||
import 'package:moxxyv2/ui/constants.dart';
|
||||
import 'package:moxxyv2/ui/helpers.dart';
|
||||
|
@ -285,6 +286,23 @@ class ConversationsPageState extends State<ConversationsPage>
|
|||
backgroundColor: primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
children: [
|
||||
SpeedDialChild(
|
||||
child: const Icon(Icons.notes),
|
||||
onTap: () {
|
||||
context.read<NewConversationBloc>().add(
|
||||
NewConversationAddedEvent(
|
||||
ikjot-2605 marked this conversation as resolved
|
||||
'',
|
||||
t.pages.conversations.speeddialAddNoteToSelf,
|
||||
'',
|
||||
ConversationType.note,
|
||||
),
|
||||
);
|
||||
},
|
||||
backgroundColor: primaryColor,
|
||||
// TODO(Unknown): Theme dependent?
|
||||
foregroundColor: Colors.white,
|
||||
label: t.pages.conversations.speeddialAddNoteToSelf,
|
||||
),
|
||||
SpeedDialChild(
|
||||
child: const Icon(Icons.group),
|
||||
onTap: () => showNotImplementedDialog('groupchat', context),
|
||||
|
|
|
@ -93,6 +93,7 @@ class NewConversationPage extends StatelessWidget {
|
|||
item.jid,
|
||||
item.title,
|
||||
item.avatarUrl,
|
||||
ConversationType.chat,
|
||||
),
|
||||
),
|
||||
child: ConversationsListRow(
|
||||
|
@ -114,6 +115,7 @@ class NewConversationPage extends StatelessWidget {
|
|||
item.avatarUrl,
|
||||
item.jid,
|
||||
0,
|
||||
ConversationType.chat,
|
||||
0,
|
||||
[],
|
||||
true,
|
||||
|
|
|
@ -88,6 +88,7 @@ class ShareSelectionPage extends StatelessWidget {
|
|||
item.avatarPath,
|
||||
item.jid,
|
||||
0,
|
||||
ConversationType.chat,
|
||||
0,
|
||||
[],
|
||||
true,
|
||||
|
|
|
@ -239,7 +239,10 @@ class AudioChatState extends State<AudioChatWidget> {
|
|||
_position,
|
||||
widget.message.id,
|
||||
),
|
||||
MessageBubbleBottom(widget.message, widget.sent),
|
||||
MessageBubbleBottom(
|
||||
widget.message,
|
||||
widget.sent,
|
||||
),
|
||||
widget.radius,
|
||||
gradient: false,
|
||||
);
|
||||
|
|
|
@ -90,7 +90,10 @@ class FileChatBaseWidget extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
MessageBubbleBottom(message, sent),
|
||||
MessageBubbleBottom(
|
||||
message,
|
||||
sent,
|
||||
),
|
||||
radius,
|
||||
gradient: false,
|
||||
//extra: extra,
|
||||
|
|
|
@ -90,7 +90,10 @@ class ImageChatWidget extends StatelessWidget {
|
|||
|
||||
return MediaBaseChatWidget(
|
||||
image,
|
||||
MessageBubbleBottom(message, sent),
|
||||
MessageBubbleBottom(
|
||||
message,
|
||||
sent,
|
||||
),
|
||||
radius,
|
||||
onTap: () => openFile(message.mediaUrl!),
|
||||
);
|
||||
|
|
|
@ -111,7 +111,11 @@ class StickerChatWidget extends StatelessWidget {
|
|||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: MessageBubbleBottom(message, sent, shrink: true),
|
||||
child: MessageBubbleBottom(
|
||||
message,
|
||||
sent,
|
||||
shrink: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -77,7 +77,10 @@ class TextChatWidget extends StatelessWidget {
|
|||
padding: topWidget != null
|
||||
? const EdgeInsets.only(left: 8, right: 8, bottom: 8)
|
||||
: EdgeInsets.zero,
|
||||
child: MessageBubbleBottom(message, sent),
|
||||
child: MessageBubbleBottom(
|
||||
message,
|
||||
sent,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
|
|
@ -89,7 +89,10 @@ class VideoChatWidget extends StatelessWidget {
|
|||
),
|
||||
borderRadius: radius,
|
||||
),
|
||||
MessageBubbleBottom(message, sent),
|
||||
MessageBubbleBottom(
|
||||
message,
|
||||
sent,
|
||||
),
|
||||
radius,
|
||||
onTap: () => openFile(message.mediaUrl!),
|
||||
extra: const PlayButton(),
|
||||
|
|
Loading…
Reference in New Issue
I think we might be able to add a new event in the NewConversationBloc that creates a Conversation with the type of
Notes
. Or we adapt the event so that we can tell it what type of chat we want. The latter might be the cleaner or the two solutions.Done.