mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 15:48:41 +00:00
fix more
This commit is contained in:
parent
f7306fe7db
commit
cfd6bd92cb
8 changed files with 192 additions and 153 deletions
|
|
@ -44,7 +44,7 @@ class _UserContextMenuState extends State<UserContextMenu> {
|
||||||
onSelect: () {
|
onSelect: () {
|
||||||
Navigator.push(context, MaterialPageRoute(
|
Navigator.push(context, MaterialPageRoute(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return ChatItemDetailsView(user: widget.contact);
|
return ChatItemDetailsView(widget.contact.userId);
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -26,20 +26,35 @@ class TwonlyDatabase extends _$TwonlyDatabase {
|
||||||
|
|
||||||
// ------------
|
// ------------
|
||||||
|
|
||||||
Stream<List<Message>> watchMessageNotOpened(int userId) {
|
Stream<List<Message>> watchMessageNotOpened(int contactId) {
|
||||||
return (select(messages)
|
return (select(messages)
|
||||||
..where((t) => t.openedAt.isNull() & t.contactId.equals(userId)))
|
..where((t) => t.openedAt.isNull() & t.contactId.equals(contactId)))
|
||||||
.watch();
|
.watch();
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<Message?> watchLastMessage(int userId) {
|
Stream<Message?> watchLastMessage(int contactId) {
|
||||||
return (select(messages)
|
return (select(messages)
|
||||||
..where((t) => t.contactId.equals(userId))
|
..where((t) => t.contactId.equals(contactId))
|
||||||
..orderBy([(t) => OrderingTerm.desc(t.sendAt)])
|
..orderBy([(t) => OrderingTerm.desc(t.sendAt)])
|
||||||
..limit(1))
|
..limit(1))
|
||||||
.watchSingleOrNull();
|
.watchSingleOrNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stream<List<Message>> watchAllMessagesFrom(int contactId) {
|
||||||
|
return (select(messages)..where((t) => t.contactId.equals(contactId)))
|
||||||
|
.watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future openedAllTextMessages(int contactId) {
|
||||||
|
final updates = MessagesCompanion(openedAt: Value(DateTime.now()));
|
||||||
|
return (update(messages)
|
||||||
|
..where((t) =>
|
||||||
|
t.contactId.equals(contactId) &
|
||||||
|
t.openedAt.isNull() &
|
||||||
|
t.kind.equals(MessageKind.textMessage.name)))
|
||||||
|
.write(updates);
|
||||||
|
}
|
||||||
|
|
||||||
// ------------
|
// ------------
|
||||||
|
|
||||||
Future<int> insertContact(ContactsCompanion contact) {
|
Future<int> insertContact(ContactsCompanion contact) {
|
||||||
|
|
@ -72,6 +87,11 @@ class TwonlyDatabase extends _$TwonlyDatabase {
|
||||||
return (select(contacts)..where((t) => t.accepted.equals(false))).watch();
|
return (select(contacts)..where((t) => t.accepted.equals(false))).watch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stream<Contact> watchContact(int userid) {
|
||||||
|
return (select(contacts)..where((t) => t.userId.equals(userid)))
|
||||||
|
.watchSingle();
|
||||||
|
}
|
||||||
|
|
||||||
Stream<List<Contact>> watchContactsForChatList() {
|
Stream<List<Contact>> watchContactsForChatList() {
|
||||||
return (select(contacts)
|
return (select(contacts)
|
||||||
..where((t) => t.accepted.equals(true) & t.blocked.equals(false))
|
..where((t) => t.accepted.equals(true) & t.blocked.equals(false))
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,9 @@ enum MessageKind {
|
||||||
ack
|
ack
|
||||||
}
|
}
|
||||||
|
|
||||||
Color getMessageColorFromType(MessageJson msg, Color primary) {
|
Color getMessageColorFromType(MessageContent content, Color primary) {
|
||||||
Color color;
|
Color color;
|
||||||
|
|
||||||
final content = msg.content;
|
|
||||||
if (content is TextMessageContent) {
|
if (content is TextMessageContent) {
|
||||||
color = Colors.lightBlue;
|
color = Colors.lightBlue;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -301,12 +301,12 @@ Future tryDownloadMedia(int messageId, int fromUserId, List<int> mediaToken,
|
||||||
apiProvider.triggerDownload(mediaToken, offset);
|
apiProvider.triggerDownload(mediaToken, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future userOpenedOtherMessage(int fromUserId, int messageOtherId) async {
|
Future notifyContactAboutOpeningMessage(int fromUserId, int messageOtherId) async {
|
||||||
await DbMessages.userOpenedOtherMessage(fromUserId, messageOtherId);
|
//await DbMessages.userOpenedOtherMessage(fromUserId, messageOtherId);
|
||||||
|
|
||||||
encryptAndSendMessage(
|
encryptAndSendMessage(
|
||||||
Int64(fromUserId),
|
fromUserId,
|
||||||
Message(
|
MessageJson(
|
||||||
kind: MessageKind.opened,
|
kind: MessageKind.opened,
|
||||||
messageId: messageOtherId,
|
messageId: messageOtherId,
|
||||||
content: MessageContent(),
|
content: MessageContent(),
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import 'package:fixnum/fixnum.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:twonly/src/components/image_editor/action_button.dart';
|
import 'package:twonly/src/components/image_editor/action_button.dart';
|
||||||
import 'package:twonly/src/components/media_view_sizing.dart';
|
import 'package:twonly/src/components/media_view_sizing.dart';
|
||||||
import 'package:twonly/src/components/notification_badge.dart';
|
import 'package:twonly/src/components/notification_badge.dart';
|
||||||
|
import 'package:twonly/src/database/contacts_db.dart';
|
||||||
|
import 'package:twonly/src/database/database.dart';
|
||||||
import 'package:twonly/src/providers/api/api.dart';
|
import 'package:twonly/src/providers/api/api.dart';
|
||||||
import 'package:twonly/src/providers/contacts_change_provider.dart';
|
|
||||||
import 'package:twonly/src/providers/send_next_media_to.dart';
|
import 'package:twonly/src/providers/send_next_media_to.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera_to_share/share_image_view.dart';
|
import 'package:twonly/src/views/camera_to_share/share_image_view.dart';
|
||||||
|
|
@ -35,6 +35,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
bool _imageSaving = false;
|
bool _imageSaving = false;
|
||||||
bool _isRealTwonly = false;
|
bool _isRealTwonly = false;
|
||||||
int _maxShowTime = 18;
|
int _maxShowTime = 18;
|
||||||
|
String? sendNextMediaToUserName;
|
||||||
|
|
||||||
ImageItem currentImage = ImageItem();
|
ImageItem currentImage = ImageItem();
|
||||||
ScreenshotController screenshotController = ScreenshotController();
|
ScreenshotController screenshotController = ScreenshotController();
|
||||||
|
|
@ -51,6 +52,15 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future updateAsync(int userId) async {
|
||||||
|
if (sendNextMediaToUserName != null) return;
|
||||||
|
Contact? contact =
|
||||||
|
await context.db.getContactByUserId(userId).getSingleOrNull();
|
||||||
|
if (contact != null) {
|
||||||
|
sendNextMediaToUserName = getContactDisplayName(contact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<Widget> get actionsAtTheRight {
|
List<Widget> get actionsAtTheRight {
|
||||||
if (layers.isNotEmpty &&
|
if (layers.isNotEmpty &&
|
||||||
layers.last.isEditing &&
|
layers.last.isEditing &&
|
||||||
|
|
@ -229,13 +239,9 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
|
|
||||||
int? sendNextMediaToUserId =
|
int? sendNextMediaToUserId =
|
||||||
context.watch<SendNextMediaTo>().sendNextMediaToUserId;
|
context.watch<SendNextMediaTo>().sendNextMediaToUserId;
|
||||||
String? sendNextMediaToUserName;
|
|
||||||
if (sendNextMediaToUserId != null) {
|
if (sendNextMediaToUserId != null) {
|
||||||
sendNextMediaToUserName = context
|
updateAsync(sendNextMediaToUserId);
|
||||||
.watch<ContactChangeProvider>()
|
|
||||||
.allContacts
|
|
||||||
.firstWhere((x) => x.userId == sendNextMediaToUserId)
|
|
||||||
.displayName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|
@ -406,7 +412,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
label: Text(
|
label: Text(
|
||||||
(sendNextMediaToUserName == null)
|
(sendNextMediaToUserName == null)
|
||||||
? context.lang.shareImagedEditorShareWith
|
? context.lang.shareImagedEditorShareWith
|
||||||
: sendNextMediaToUserName,
|
: sendNextMediaToUserName!,
|
||||||
style: TextStyle(fontSize: 17),
|
style: TextStyle(fontSize: 17),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
import 'dart:convert';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
@ -6,13 +8,11 @@ import 'package:twonly/src/components/animate_icon.dart';
|
||||||
import 'package:twonly/src/components/initialsavatar.dart';
|
import 'package:twonly/src/components/initialsavatar.dart';
|
||||||
import 'package:twonly/src/components/message_send_state_icon.dart';
|
import 'package:twonly/src/components/message_send_state_icon.dart';
|
||||||
import 'package:twonly/src/components/verified_shield.dart';
|
import 'package:twonly/src/components/verified_shield.dart';
|
||||||
import '../../../../.blocked/archives/contacts_model.dart';
|
import 'package:twonly/src/database/contacts_db.dart';
|
||||||
|
import 'package:twonly/src/database/database.dart';
|
||||||
|
import 'package:twonly/src/database/messages_db.dart';
|
||||||
import 'package:twonly/src/model/json/message.dart';
|
import 'package:twonly/src/model/json/message.dart';
|
||||||
import '../../../../.blocked/archives/messages_model.dart';
|
|
||||||
import 'package:twonly/src/providers/api/api.dart';
|
import 'package:twonly/src/providers/api/api.dart';
|
||||||
import 'package:twonly/src/providers/contacts_change_provider.dart';
|
|
||||||
import 'package:twonly/src/providers/download_change_provider.dart';
|
|
||||||
import 'package:twonly/src/providers/messages_change_provider.dart';
|
|
||||||
import 'package:twonly/src/providers/send_next_media_to.dart';
|
import 'package:twonly/src/providers/send_next_media_to.dart';
|
||||||
import 'package:twonly/src/services/notification_service.dart';
|
import 'package:twonly/src/services/notification_service.dart';
|
||||||
import 'package:twonly/src/views/chats/media_viewer_view.dart';
|
import 'package:twonly/src/views/chats/media_viewer_view.dart';
|
||||||
|
|
@ -21,27 +21,25 @@ import 'package:twonly/src/views/contact/contact_view.dart';
|
||||||
import 'package:twonly/src/views/home_view.dart';
|
import 'package:twonly/src/views/home_view.dart';
|
||||||
|
|
||||||
class ChatListEntry extends StatelessWidget {
|
class ChatListEntry extends StatelessWidget {
|
||||||
const ChatListEntry(this.message, this.user, this.lastMessageFromSameUser,
|
const ChatListEntry(this.message, this.userId, this.lastMessageFromSameUser,
|
||||||
{super.key});
|
{super.key});
|
||||||
final DbMessage message;
|
final Message message;
|
||||||
final Contact user;
|
final int userId;
|
||||||
final bool lastMessageFromSameUser;
|
final bool lastMessageFromSameUser;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool right = message.messageOtherId == null;
|
bool right = message.messageOtherId == null;
|
||||||
MessageSendState state = message.getSendState();
|
MessageSendState state = messageSendStateFromMessage(message);
|
||||||
|
|
||||||
bool isDownloading = false;
|
bool isDownloading = false;
|
||||||
List<int> token = [];
|
List<int> token = [];
|
||||||
|
|
||||||
final content = message.messageContent;
|
final messageJson = MessageJson.fromJson(jsonDecode(message.contentJson!));
|
||||||
if (message.messageReceived && content is MediaMessageContent) {
|
final content = messageJson.content;
|
||||||
|
if (message.messageOtherId != null && content is MediaMessageContent) {
|
||||||
token = content.downloadToken;
|
token = content.downloadToken;
|
||||||
isDownloading = context
|
isDownloading = message.downloadState == DownloadState.downloading;
|
||||||
.watch<DownloadChangeProvider>()
|
|
||||||
.currentlyDownloading
|
|
||||||
.contains(token.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget child = Container();
|
Widget child = Container();
|
||||||
|
|
@ -81,20 +79,21 @@ class ChatListEntry extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (content is MediaMessageContent && !content.isVideo) {
|
} else if (content is MediaMessageContent && !content.isVideo) {
|
||||||
Color color = message.messageContent
|
Color color = getMessageColorFromType(
|
||||||
.getColor(Theme.of(context).colorScheme.primary);
|
content, Theme.of(context).colorScheme.primary);
|
||||||
|
|
||||||
child = GestureDetector(
|
child = GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (state == MessageSendState.received && !isDownloading) {
|
if (state == MessageSendState.received && !isDownloading) {
|
||||||
if (message.isDownloaded) {
|
if (message.downloadState == DownloadState.downloaded) {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(builder: (context) {
|
MaterialPageRoute(builder: (context) {
|
||||||
return MediaViewerView(user, message);
|
return MediaViewerView(userId);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
tryDownloadMedia(message.messageId, message.otherUserId, token,
|
tryDownloadMedia(message.messageId, message.contactId, token,
|
||||||
force: true);
|
force: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -112,7 +111,7 @@ class ChatListEntry extends StatelessWidget {
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: MessageSendStateIcon(
|
child: MessageSendStateIcon(
|
||||||
message,
|
[message],
|
||||||
mainAxisAlignment:
|
mainAxisAlignment:
|
||||||
right ? MainAxisAlignment.center : MainAxisAlignment.center,
|
right ? MainAxisAlignment.center : MainAxisAlignment.center,
|
||||||
),
|
),
|
||||||
|
|
@ -134,9 +133,9 @@ class ChatListEntry extends StatelessWidget {
|
||||||
|
|
||||||
/// Displays detailed information about a SampleItem.
|
/// Displays detailed information about a SampleItem.
|
||||||
class ChatItemDetailsView extends StatefulWidget {
|
class ChatItemDetailsView extends StatefulWidget {
|
||||||
const ChatItemDetailsView({super.key, required this.user});
|
const ChatItemDetailsView(this.userid, {super.key});
|
||||||
|
|
||||||
final Contact user;
|
final int userid;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ChatItemDetailsView> createState() => _ChatItemDetailsViewState();
|
State<ChatItemDetailsView> createState() => _ChatItemDetailsViewState();
|
||||||
|
|
@ -145,86 +144,99 @@ class ChatItemDetailsView extends StatefulWidget {
|
||||||
class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
|
class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
|
||||||
TextEditingController newMessageController = TextEditingController();
|
TextEditingController newMessageController = TextEditingController();
|
||||||
HashSet<int> alreadyReportedOpened = HashSet<int>();
|
HashSet<int> alreadyReportedOpened = HashSet<int>();
|
||||||
late Contact user;
|
Contact? user;
|
||||||
String currentInputText = "";
|
String currentInputText = "";
|
||||||
|
late StreamSubscription<Contact> userSub;
|
||||||
|
late StreamSubscription<List<Message>> messageSub;
|
||||||
|
List<Message> messages = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
user = widget.user;
|
initStreams();
|
||||||
context
|
|
||||||
.read<MessagesChangeProvider>()
|
|
||||||
.loadMessagesForUser(user.userId.toInt());
|
|
||||||
initAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future initAsync() async {
|
@override
|
||||||
context
|
void dispose() {
|
||||||
.read<MessagesChangeProvider>()
|
super.dispose();
|
||||||
.loadMessagesForUser(user.userId.toInt(), force: true);
|
userSub.cancel();
|
||||||
setState(() {});
|
messageSub.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future initStreams() async {
|
||||||
|
Stream<Contact> contact = context.db.watchContact(widget.userid);
|
||||||
|
userSub = contact.listen((contact) {
|
||||||
|
setState(() {
|
||||||
|
user = contact;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Stream<List<Message>> msgStream =
|
||||||
|
context.db.watchAllMessagesFrom(widget.userid);
|
||||||
|
messageSub = msgStream.listen((msgs) {
|
||||||
|
if (!context.mounted) return;
|
||||||
|
var updated = false;
|
||||||
|
for (Message msg in msgs) {
|
||||||
|
if (msg.kind == MessageKind.textMessage &&
|
||||||
|
msg.messageOtherId != null &&
|
||||||
|
msg.openedAt == null) {
|
||||||
|
updated = true;
|
||||||
|
flutterLocalNotificationsPlugin.cancel(msg.messageId);
|
||||||
|
notifyContactAboutOpeningMessage(widget.userid, msg.messageOtherId!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (updated) {
|
||||||
|
context.db.openedAllTextMessages(widget.userid);
|
||||||
|
} else {
|
||||||
|
// The stream should be get an update, so only update the UI when all are opened
|
||||||
|
setState(() {
|
||||||
|
messages = msgs;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future _sendMessage() async {
|
Future _sendMessage() async {
|
||||||
if (newMessageController.text == "") return;
|
if (newMessageController.text == "" || user == null) return;
|
||||||
setState(() {});
|
await sendTextMessage(user!.userId, newMessageController.text);
|
||||||
await sendTextMessage(user.userId, newMessageController.text);
|
|
||||||
newMessageController.clear();
|
newMessageController.clear();
|
||||||
currentInputText = "";
|
currentInputText = "";
|
||||||
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
user = context
|
|
||||||
.watch<ContactChangeProvider>()
|
|
||||||
.allContacts
|
|
||||||
.firstWhere((c) => c.userId == widget.user.userId);
|
|
||||||
|
|
||||||
List<DbMessage> messages = context
|
|
||||||
.watch<MessagesChangeProvider>()
|
|
||||||
.allMessagesFromUser[user.userId.toInt()] ??
|
|
||||||
[];
|
|
||||||
|
|
||||||
messages.where((x) => x.messageOpenedAt == null).forEach((message) {
|
|
||||||
if (message.messageOtherId != null &&
|
|
||||||
message.messageContent is TextMessageContent) {
|
|
||||||
if (!alreadyReportedOpened.contains(message.messageOtherId!)) {
|
|
||||||
userOpenedOtherMessage(message.otherUserId, message.messageOtherId!);
|
|
||||||
flutterLocalNotificationsPlugin.cancel(message.messageId);
|
|
||||||
alreadyReportedOpened.add(message.messageOtherId!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: GestureDetector(
|
title: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (context) {
|
Navigator.push(context, MaterialPageRoute(builder: (context) {
|
||||||
return ContactView(user.userId.toInt());
|
return ContactView(widget.userid);
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
child: Row(
|
child: (user == null)
|
||||||
children: [
|
? Container()
|
||||||
InitialsAvatar(
|
: Row(
|
||||||
displayName: user.displayName,
|
children: [
|
||||||
fontSize: 19,
|
InitialsAvatar(
|
||||||
),
|
getContactDisplayName(user!),
|
||||||
SizedBox(width: 10),
|
fontSize: 19,
|
||||||
Expanded(
|
),
|
||||||
child: Container(
|
SizedBox(width: 10),
|
||||||
color: Colors.transparent,
|
Expanded(
|
||||||
child: Row(
|
child: Container(
|
||||||
children: [
|
color: Colors.transparent,
|
||||||
Text(user.displayName),
|
child: Row(
|
||||||
SizedBox(width: 10),
|
children: [
|
||||||
VerifiedShield(user),
|
Text(getContactDisplayName(user!)),
|
||||||
],
|
SizedBox(width: 10),
|
||||||
),
|
VerifiedShield(user!),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
|
|
@ -242,17 +254,17 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
|
||||||
(messages[i - 1].messageOtherId != null &&
|
(messages[i - 1].messageOtherId != null &&
|
||||||
messages[i].messageOtherId != null);
|
messages[i].messageOtherId != null);
|
||||||
}
|
}
|
||||||
if (messages[i].messageOpenedAt != null) {
|
// if (messages[i].openedAt != null) {
|
||||||
if (calculateTimeDifference(
|
// if (calculateTimeDifference(
|
||||||
DateTime.now(), messages[i].messageOpenedAt!)
|
// DateTime.now(), messages[i].openedAt!)
|
||||||
.inHours >=
|
// .inHours >=
|
||||||
24) {
|
// 24) {
|
||||||
return Container();
|
// return Container();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return ChatListEntry(
|
return ChatListEntry(
|
||||||
messages[i],
|
messages[i],
|
||||||
user,
|
widget.userid,
|
||||||
lastMessageFromSameUser,
|
lastMessageFromSameUser,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -310,8 +322,9 @@ class _ChatItemDetailsViewState extends State<ChatItemDetailsView> {
|
||||||
: IconButton(
|
: IconButton(
|
||||||
icon: FaIcon(FontAwesomeIcons.camera),
|
icon: FaIcon(FontAwesomeIcons.camera),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.read<SendNextMediaTo>().updateSendNextMediaTo(
|
context
|
||||||
widget.user.userId.toInt());
|
.read<SendNextMediaTo>()
|
||||||
|
.updateSendNextMediaTo(widget.userid);
|
||||||
globalUpdateOfHomeViewPageIndex(0);
|
globalUpdateOfHomeViewPageIndex(0);
|
||||||
Navigator.popUntil(context, (route) => route.isFirst);
|
Navigator.popUntil(context, (route) => route.isFirst);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -283,7 +283,7 @@ class _UserListItem extends State<UserListItem> {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(builder: (context) {
|
MaterialPageRoute(builder: (context) {
|
||||||
return MediaViewerView(widget.user, msg);
|
return MediaViewerView(widget.user.userId);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|
@ -291,7 +291,7 @@ class _UserListItem extends State<UserListItem> {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(builder: (context) {
|
MaterialPageRoute(builder: (context) {
|
||||||
return ChatItemDetailsView(user: widget.user);
|
return ChatItemDetailsView(widget.user.userId);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
|
@ -7,11 +8,10 @@ import 'package:no_screenshot/no_screenshot.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:twonly/src/components/animate_icon.dart';
|
import 'package:twonly/src/components/animate_icon.dart';
|
||||||
import 'package:twonly/src/components/media_view_sizing.dart';
|
import 'package:twonly/src/components/media_view_sizing.dart';
|
||||||
import '../../../../.blocked/archives/contacts_model.dart';
|
import 'package:twonly/src/database/database.dart';
|
||||||
|
import 'package:twonly/src/database/messages_db.dart';
|
||||||
import 'package:twonly/src/model/json/message.dart';
|
import 'package:twonly/src/model/json/message.dart';
|
||||||
import '../../../../.blocked/archives/messages_model.dart';
|
|
||||||
import 'package:twonly/src/providers/api/api.dart';
|
import 'package:twonly/src/providers/api/api.dart';
|
||||||
import 'package:twonly/src/providers/messages_change_provider.dart';
|
|
||||||
import 'package:twonly/src/providers/send_next_media_to.dart';
|
import 'package:twonly/src/providers/send_next_media_to.dart';
|
||||||
import 'package:twonly/src/services/notification_service.dart';
|
import 'package:twonly/src/services/notification_service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
|
@ -21,9 +21,8 @@ import 'package:twonly/src/views/home_view.dart';
|
||||||
final _noScreenshot = NoScreenshot.instance;
|
final _noScreenshot = NoScreenshot.instance;
|
||||||
|
|
||||||
class MediaViewerView extends StatefulWidget {
|
class MediaViewerView extends StatefulWidget {
|
||||||
final Contact otherUser;
|
final int userId;
|
||||||
final DbMessage message;
|
const MediaViewerView(this.userId, {super.key});
|
||||||
const MediaViewerView(this.otherUser, this.message, {super.key});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MediaViewerView> createState() => _MediaViewerViewState();
|
State<MediaViewerView> createState() => _MediaViewerViewState();
|
||||||
|
|
@ -44,35 +43,29 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
bool isRealTwonly = false;
|
bool isRealTwonly = false;
|
||||||
bool isDownloading = false;
|
bool isDownloading = false;
|
||||||
|
|
||||||
List<DbMessage> allMediaFiles = [];
|
List<Message> allMediaFiles = [];
|
||||||
|
late StreamSubscription<List<Message>> _subscription;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
allMediaFiles = [widget.message];
|
|
||||||
asyncLoadNextMedia();
|
asyncLoadNextMedia();
|
||||||
loadCurrentMediaFile();
|
loadCurrentMediaFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future asyncLoadNextMedia() async {
|
Future asyncLoadNextMedia() async {
|
||||||
await context
|
Stream<List<Message>> messages =
|
||||||
.read<MessagesChangeProvider>()
|
context.db.watchMessageNotOpened(widget.userId);
|
||||||
.loadMessagesForUser(widget.otherUser.userId.toInt());
|
|
||||||
if (!context.mounted) return;
|
_subscription = messages.listen((messages) {
|
||||||
final allMessages = context
|
for (Message msg in messages) {
|
||||||
.read<MessagesChangeProvider>()
|
if (!allMediaFiles.any((m) => m.messageId == msg.messageId)) {
|
||||||
.allMessagesFromUser[widget.otherUser.userId.toInt()];
|
allMediaFiles.add(msg);
|
||||||
if (allMessages == null) {
|
}
|
||||||
return;
|
}
|
||||||
}
|
setState(() {});
|
||||||
final nextMediaFiles = allMessages.where((x) =>
|
});
|
||||||
x.isMedia() &&
|
|
||||||
x.messageOtherId != null &&
|
|
||||||
x.messageOpenedAt == null &&
|
|
||||||
x.messageId != widget.message.messageId);
|
|
||||||
allMediaFiles.addAll(nextMediaFiles.map((x) => x));
|
|
||||||
setState(() {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future nextMediaOrExit() async {
|
Future nextMediaOrExit() async {
|
||||||
|
|
@ -92,7 +85,10 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
await _noScreenshot.screenshotOff();
|
await _noScreenshot.screenshotOff();
|
||||||
if (!context.mounted || allMediaFiles.isEmpty) return;
|
if (!context.mounted || allMediaFiles.isEmpty) return;
|
||||||
|
|
||||||
final DbMessage current = allMediaFiles.first;
|
final Message current = allMediaFiles.first;
|
||||||
|
final MessageJson messageJson =
|
||||||
|
MessageJson.fromJson(jsonDecode(current.contentJson!));
|
||||||
|
final MessageContent? content = messageJson.content;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
// reset current image values
|
// reset current image values
|
||||||
|
|
@ -101,16 +97,19 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
maxShowTime = 999999;
|
maxShowTime = 999999;
|
||||||
progress = 0;
|
progress = 0;
|
||||||
isDownloading = false;
|
isDownloading = false;
|
||||||
isRealTwonly = current.isRealTwonly();
|
isRealTwonly = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// This will show the extra screen for the twonly
|
|
||||||
if (current.isRealTwonly() && !showTwonly) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final content = current.messageContent;
|
|
||||||
if (content is MediaMessageContent) {
|
if (content is MediaMessageContent) {
|
||||||
|
if (content.isRealTwonly) {
|
||||||
|
setState(() {
|
||||||
|
isRealTwonly = true;
|
||||||
|
});
|
||||||
|
if (!showTwonly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isRealTwonly) {
|
if (isRealTwonly) {
|
||||||
bool isAuth = await authenticateUser(context.lang.mediaViewerAuthReason,
|
bool isAuth = await authenticateUser(context.lang.mediaViewerAuthReason,
|
||||||
force: false);
|
force: false);
|
||||||
|
|
@ -120,22 +119,22 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flutterLocalNotificationsPlugin.cancel(current.messageId);
|
flutterLocalNotificationsPlugin.cancel(current.messageId);
|
||||||
if (!current.isDownloaded) {
|
if (current.downloadState == DownloadState.pending) {
|
||||||
setState(() {
|
setState(() {
|
||||||
isDownloading = true;
|
isDownloading = true;
|
||||||
});
|
});
|
||||||
await tryDownloadMedia(
|
await tryDownloadMedia(
|
||||||
current.messageId, current.otherUserId, content.downloadToken,
|
current.messageId, current.contactId, content.downloadToken,
|
||||||
force: true);
|
force: true);
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
if (isDownloading) {
|
if (isDownloading) {
|
||||||
await Future.delayed(Duration(milliseconds: 100));
|
await Future.delayed(Duration(milliseconds: 10));
|
||||||
}
|
}
|
||||||
imageBytes = await getDownloadedMedia(
|
imageBytes = await getDownloadedMedia(
|
||||||
content.downloadToken,
|
content.downloadToken,
|
||||||
current.messageOtherId!,
|
current.messageOtherId!,
|
||||||
current.otherUserId,
|
current.contactId,
|
||||||
);
|
);
|
||||||
} while (isDownloading && imageBytes == null);
|
} while (isDownloading && imageBytes == null);
|
||||||
|
|
||||||
|
|
@ -181,6 +180,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
nextMediaTimer?.cancel();
|
nextMediaTimer?.cancel();
|
||||||
progressTimer?.cancel();
|
progressTimer?.cancel();
|
||||||
_noScreenshot.screenshotOn();
|
_noScreenshot.screenshotOn();
|
||||||
|
_subscription.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -303,7 +303,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
curve: Curves.linearToEaseOut,
|
curve: Curves.linearToEaseOut,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
sendTextMessage(widget.otherUser.userId, emoji);
|
sendTextMessage(widget.userId, emoji);
|
||||||
setState(() {
|
setState(() {
|
||||||
selectedShortReaction = index;
|
selectedShortReaction = index;
|
||||||
});
|
});
|
||||||
|
|
@ -351,8 +351,9 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
IconButton.outlined(
|
IconButton.outlined(
|
||||||
icon: FaIcon(FontAwesomeIcons.camera),
|
icon: FaIcon(FontAwesomeIcons.camera),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
context.read<SendNextMediaTo>().updateSendNextMediaTo(
|
context
|
||||||
widget.otherUser.userId.toInt());
|
.read<SendNextMediaTo>()
|
||||||
|
.updateSendNextMediaTo(widget.userId.toInt());
|
||||||
globalUpdateOfHomeViewPageIndex(0);
|
globalUpdateOfHomeViewPageIndex(0);
|
||||||
Navigator.popUntil(context, (route) => route.isFirst);
|
Navigator.popUntil(context, (route) => route.isFirst);
|
||||||
},
|
},
|
||||||
|
|
@ -410,7 +411,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(builder: (context) {
|
MaterialPageRoute(builder: (context) {
|
||||||
return ChatItemDetailsView(user: widget.otherUser);
|
return ChatItemDetailsView(widget.userId);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue