mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 11:18:41 +00:00
fixing memories
This commit is contained in:
parent
1c154e6c67
commit
84828cd820
21 changed files with 347 additions and 413 deletions
|
|
@ -6,7 +6,6 @@ import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/localization/generated/app_localizations.dart';
|
import 'package:twonly/src/localization/generated/app_localizations.dart';
|
||||||
import 'package:twonly/src/providers/connection.provider.dart';
|
import 'package:twonly/src/providers/connection.provider.dart';
|
||||||
import 'package:twonly/src/providers/settings.provider.dart';
|
import 'package:twonly/src/providers/settings.provider.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
import 'package:twonly/src/views/components/app_outdated.dart';
|
import 'package:twonly/src/views/components/app_outdated.dart';
|
||||||
import 'package:twonly/src/views/home.view.dart';
|
import 'package:twonly/src/views/home.view.dart';
|
||||||
|
|
@ -68,8 +67,6 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||||
await setUserPlan();
|
await setUserPlan();
|
||||||
await apiService.connect(force: true);
|
await apiService.connect(force: true);
|
||||||
await apiService.listenToNetworkChanges();
|
await apiService.listenToNetworkChanges();
|
||||||
// call this function so invalid media files are get purged
|
|
||||||
await retryMediaUpload(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -84,7 +81,6 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||||
} else if (state == AppLifecycleState.paused) {
|
} else if (state == AppLifecycleState.paused) {
|
||||||
wasPaused = true;
|
wasPaused = true;
|
||||||
globalIsAppInBackground = true;
|
globalIsAppInBackground = true;
|
||||||
unawaited(handleUploadWhenAppGoesBackground());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,14 @@ class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> deleteMediaFile(String mediaId) async {
|
||||||
|
await (delete(mediaFiles)
|
||||||
|
..where(
|
||||||
|
(t) => t.mediaId.equals(mediaId),
|
||||||
|
))
|
||||||
|
.go();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> updateMedia(
|
Future<void> updateMedia(
|
||||||
String mediaId,
|
String mediaId,
|
||||||
MediaFilesCompanion updates,
|
MediaFilesCompanion updates,
|
||||||
|
|
@ -57,4 +65,8 @@ class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
|
||||||
..where((t) => t.downloadState.equals(DownloadState.pending.name)))
|
..where((t) => t.downloadState.equals(DownloadState.pending.name)))
|
||||||
.get();
|
.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stream<List<MediaFile>> watchAllStoredMediaFiles() {
|
||||||
|
return (select(mediaFiles)..where((t) => t.stored.equals(true))).watch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import 'package:twonly/src/database/tables/contacts.table.dart';
|
||||||
import 'package:twonly/src/database/tables/groups.table.dart';
|
import 'package:twonly/src/database/tables/groups.table.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
|
|
||||||
|
enum MessageType { media, text }
|
||||||
|
|
||||||
@DataClassName('Message')
|
@DataClassName('Message')
|
||||||
class Messages extends Table {
|
class Messages extends Table {
|
||||||
TextColumn get groupId =>
|
TextColumn get groupId =>
|
||||||
|
|
@ -14,9 +16,12 @@ class Messages extends Table {
|
||||||
IntColumn get senderId =>
|
IntColumn get senderId =>
|
||||||
integer().nullable().references(Contacts, #userId)();
|
integer().nullable().references(Contacts, #userId)();
|
||||||
|
|
||||||
|
TextColumn get type => textEnum<MessageType>()();
|
||||||
|
|
||||||
TextColumn get content => text().nullable()();
|
TextColumn get content => text().nullable()();
|
||||||
TextColumn get mediaId =>
|
TextColumn get mediaId => text()
|
||||||
text().nullable().references(MediaFiles, #mediaId)();
|
.nullable()
|
||||||
|
.references(MediaFiles, #mediaId, onDelete: KeyAction.cascade)();
|
||||||
|
|
||||||
BoolColumn get mediaStored => boolean().withDefault(const Constant(false))();
|
BoolColumn get mediaStored => boolean().withDefault(const Constant(false))();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2254,6 +2254,11 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultConstraints:
|
defaultConstraints:
|
||||||
GeneratedColumn.constraintIsAlways('REFERENCES contacts (user_id)'));
|
GeneratedColumn.constraintIsAlways('REFERENCES contacts (user_id)'));
|
||||||
|
@override
|
||||||
|
late final GeneratedColumnWithTypeConverter<MessageType, String> type =
|
||||||
|
GeneratedColumn<String>('type', aliasedName, false,
|
||||||
|
type: DriftSqlType.string, requiredDuringInsert: true)
|
||||||
|
.withConverter<MessageType>($MessagesTable.$convertertype);
|
||||||
static const VerificationMeta _contentMeta =
|
static const VerificationMeta _contentMeta =
|
||||||
const VerificationMeta('content');
|
const VerificationMeta('content');
|
||||||
@override
|
@override
|
||||||
|
|
@ -2268,7 +2273,7 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
|
||||||
type: DriftSqlType.string,
|
type: DriftSqlType.string,
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||||
'REFERENCES media_files (media_id)'));
|
'REFERENCES media_files (media_id) ON DELETE CASCADE'));
|
||||||
static const VerificationMeta _mediaStoredMeta =
|
static const VerificationMeta _mediaStoredMeta =
|
||||||
const VerificationMeta('mediaStored');
|
const VerificationMeta('mediaStored');
|
||||||
@override
|
@override
|
||||||
|
|
@ -2327,6 +2332,7 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
|
||||||
groupId,
|
groupId,
|
||||||
messageId,
|
messageId,
|
||||||
senderId,
|
senderId,
|
||||||
|
type,
|
||||||
content,
|
content,
|
||||||
mediaId,
|
mediaId,
|
||||||
mediaStored,
|
mediaStored,
|
||||||
|
|
@ -2415,6 +2421,8 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
|
||||||
.read(DriftSqlType.string, data['${effectivePrefix}message_id'])!,
|
.read(DriftSqlType.string, data['${effectivePrefix}message_id'])!,
|
||||||
senderId: attachedDatabase.typeMapping
|
senderId: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.int, data['${effectivePrefix}sender_id']),
|
.read(DriftSqlType.int, data['${effectivePrefix}sender_id']),
|
||||||
|
type: $MessagesTable.$convertertype.fromSql(attachedDatabase.typeMapping
|
||||||
|
.read(DriftSqlType.string, data['${effectivePrefix}type'])!),
|
||||||
content: attachedDatabase.typeMapping
|
content: attachedDatabase.typeMapping
|
||||||
.read(DriftSqlType.string, data['${effectivePrefix}content']),
|
.read(DriftSqlType.string, data['${effectivePrefix}content']),
|
||||||
mediaId: attachedDatabase.typeMapping
|
mediaId: attachedDatabase.typeMapping
|
||||||
|
|
@ -2438,12 +2446,16 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
|
||||||
$MessagesTable createAlias(String alias) {
|
$MessagesTable createAlias(String alias) {
|
||||||
return $MessagesTable(attachedDatabase, alias);
|
return $MessagesTable(attachedDatabase, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JsonTypeConverter2<MessageType, String, String> $convertertype =
|
||||||
|
const EnumNameConverter<MessageType>(MessageType.values);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Message extends DataClass implements Insertable<Message> {
|
class Message extends DataClass implements Insertable<Message> {
|
||||||
final String groupId;
|
final String groupId;
|
||||||
final String messageId;
|
final String messageId;
|
||||||
final int? senderId;
|
final int? senderId;
|
||||||
|
final MessageType type;
|
||||||
final String? content;
|
final String? content;
|
||||||
final String? mediaId;
|
final String? mediaId;
|
||||||
final bool mediaStored;
|
final bool mediaStored;
|
||||||
|
|
@ -2456,6 +2468,7 @@ class Message extends DataClass implements Insertable<Message> {
|
||||||
{required this.groupId,
|
{required this.groupId,
|
||||||
required this.messageId,
|
required this.messageId,
|
||||||
this.senderId,
|
this.senderId,
|
||||||
|
required this.type,
|
||||||
this.content,
|
this.content,
|
||||||
this.mediaId,
|
this.mediaId,
|
||||||
required this.mediaStored,
|
required this.mediaStored,
|
||||||
|
|
@ -2472,6 +2485,9 @@ class Message extends DataClass implements Insertable<Message> {
|
||||||
if (!nullToAbsent || senderId != null) {
|
if (!nullToAbsent || senderId != null) {
|
||||||
map['sender_id'] = Variable<int>(senderId);
|
map['sender_id'] = Variable<int>(senderId);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
map['type'] = Variable<String>($MessagesTable.$convertertype.toSql(type));
|
||||||
|
}
|
||||||
if (!nullToAbsent || content != null) {
|
if (!nullToAbsent || content != null) {
|
||||||
map['content'] = Variable<String>(content);
|
map['content'] = Variable<String>(content);
|
||||||
}
|
}
|
||||||
|
|
@ -2498,6 +2514,7 @@ class Message extends DataClass implements Insertable<Message> {
|
||||||
senderId: senderId == null && nullToAbsent
|
senderId: senderId == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(senderId),
|
: Value(senderId),
|
||||||
|
type: Value(type),
|
||||||
content: content == null && nullToAbsent
|
content: content == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(content),
|
: Value(content),
|
||||||
|
|
@ -2524,6 +2541,8 @@ class Message extends DataClass implements Insertable<Message> {
|
||||||
groupId: serializer.fromJson<String>(json['groupId']),
|
groupId: serializer.fromJson<String>(json['groupId']),
|
||||||
messageId: serializer.fromJson<String>(json['messageId']),
|
messageId: serializer.fromJson<String>(json['messageId']),
|
||||||
senderId: serializer.fromJson<int?>(json['senderId']),
|
senderId: serializer.fromJson<int?>(json['senderId']),
|
||||||
|
type: $MessagesTable.$convertertype
|
||||||
|
.fromJson(serializer.fromJson<String>(json['type'])),
|
||||||
content: serializer.fromJson<String?>(json['content']),
|
content: serializer.fromJson<String?>(json['content']),
|
||||||
mediaId: serializer.fromJson<String?>(json['mediaId']),
|
mediaId: serializer.fromJson<String?>(json['mediaId']),
|
||||||
mediaStored: serializer.fromJson<bool>(json['mediaStored']),
|
mediaStored: serializer.fromJson<bool>(json['mediaStored']),
|
||||||
|
|
@ -2542,6 +2561,8 @@ class Message extends DataClass implements Insertable<Message> {
|
||||||
'groupId': serializer.toJson<String>(groupId),
|
'groupId': serializer.toJson<String>(groupId),
|
||||||
'messageId': serializer.toJson<String>(messageId),
|
'messageId': serializer.toJson<String>(messageId),
|
||||||
'senderId': serializer.toJson<int?>(senderId),
|
'senderId': serializer.toJson<int?>(senderId),
|
||||||
|
'type':
|
||||||
|
serializer.toJson<String>($MessagesTable.$convertertype.toJson(type)),
|
||||||
'content': serializer.toJson<String?>(content),
|
'content': serializer.toJson<String?>(content),
|
||||||
'mediaId': serializer.toJson<String?>(mediaId),
|
'mediaId': serializer.toJson<String?>(mediaId),
|
||||||
'mediaStored': serializer.toJson<bool>(mediaStored),
|
'mediaStored': serializer.toJson<bool>(mediaStored),
|
||||||
|
|
@ -2557,6 +2578,7 @@ class Message extends DataClass implements Insertable<Message> {
|
||||||
{String? groupId,
|
{String? groupId,
|
||||||
String? messageId,
|
String? messageId,
|
||||||
Value<int?> senderId = const Value.absent(),
|
Value<int?> senderId = const Value.absent(),
|
||||||
|
MessageType? type,
|
||||||
Value<String?> content = const Value.absent(),
|
Value<String?> content = const Value.absent(),
|
||||||
Value<String?> mediaId = const Value.absent(),
|
Value<String?> mediaId = const Value.absent(),
|
||||||
bool? mediaStored,
|
bool? mediaStored,
|
||||||
|
|
@ -2569,6 +2591,7 @@ class Message extends DataClass implements Insertable<Message> {
|
||||||
groupId: groupId ?? this.groupId,
|
groupId: groupId ?? this.groupId,
|
||||||
messageId: messageId ?? this.messageId,
|
messageId: messageId ?? this.messageId,
|
||||||
senderId: senderId.present ? senderId.value : this.senderId,
|
senderId: senderId.present ? senderId.value : this.senderId,
|
||||||
|
type: type ?? this.type,
|
||||||
content: content.present ? content.value : this.content,
|
content: content.present ? content.value : this.content,
|
||||||
mediaId: mediaId.present ? mediaId.value : this.mediaId,
|
mediaId: mediaId.present ? mediaId.value : this.mediaId,
|
||||||
mediaStored: mediaStored ?? this.mediaStored,
|
mediaStored: mediaStored ?? this.mediaStored,
|
||||||
|
|
@ -2586,6 +2609,7 @@ class Message extends DataClass implements Insertable<Message> {
|
||||||
groupId: data.groupId.present ? data.groupId.value : this.groupId,
|
groupId: data.groupId.present ? data.groupId.value : this.groupId,
|
||||||
messageId: data.messageId.present ? data.messageId.value : this.messageId,
|
messageId: data.messageId.present ? data.messageId.value : this.messageId,
|
||||||
senderId: data.senderId.present ? data.senderId.value : this.senderId,
|
senderId: data.senderId.present ? data.senderId.value : this.senderId,
|
||||||
|
type: data.type.present ? data.type.value : this.type,
|
||||||
content: data.content.present ? data.content.value : this.content,
|
content: data.content.present ? data.content.value : this.content,
|
||||||
mediaId: data.mediaId.present ? data.mediaId.value : this.mediaId,
|
mediaId: data.mediaId.present ? data.mediaId.value : this.mediaId,
|
||||||
mediaStored:
|
mediaStored:
|
||||||
|
|
@ -2610,6 +2634,7 @@ class Message extends DataClass implements Insertable<Message> {
|
||||||
..write('groupId: $groupId, ')
|
..write('groupId: $groupId, ')
|
||||||
..write('messageId: $messageId, ')
|
..write('messageId: $messageId, ')
|
||||||
..write('senderId: $senderId, ')
|
..write('senderId: $senderId, ')
|
||||||
|
..write('type: $type, ')
|
||||||
..write('content: $content, ')
|
..write('content: $content, ')
|
||||||
..write('mediaId: $mediaId, ')
|
..write('mediaId: $mediaId, ')
|
||||||
..write('mediaStored: $mediaStored, ')
|
..write('mediaStored: $mediaStored, ')
|
||||||
|
|
@ -2627,6 +2652,7 @@ class Message extends DataClass implements Insertable<Message> {
|
||||||
groupId,
|
groupId,
|
||||||
messageId,
|
messageId,
|
||||||
senderId,
|
senderId,
|
||||||
|
type,
|
||||||
content,
|
content,
|
||||||
mediaId,
|
mediaId,
|
||||||
mediaStored,
|
mediaStored,
|
||||||
|
|
@ -2642,6 +2668,7 @@ class Message extends DataClass implements Insertable<Message> {
|
||||||
other.groupId == this.groupId &&
|
other.groupId == this.groupId &&
|
||||||
other.messageId == this.messageId &&
|
other.messageId == this.messageId &&
|
||||||
other.senderId == this.senderId &&
|
other.senderId == this.senderId &&
|
||||||
|
other.type == this.type &&
|
||||||
other.content == this.content &&
|
other.content == this.content &&
|
||||||
other.mediaId == this.mediaId &&
|
other.mediaId == this.mediaId &&
|
||||||
other.mediaStored == this.mediaStored &&
|
other.mediaStored == this.mediaStored &&
|
||||||
|
|
@ -2656,6 +2683,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
||||||
final Value<String> groupId;
|
final Value<String> groupId;
|
||||||
final Value<String> messageId;
|
final Value<String> messageId;
|
||||||
final Value<int?> senderId;
|
final Value<int?> senderId;
|
||||||
|
final Value<MessageType> type;
|
||||||
final Value<String?> content;
|
final Value<String?> content;
|
||||||
final Value<String?> mediaId;
|
final Value<String?> mediaId;
|
||||||
final Value<bool> mediaStored;
|
final Value<bool> mediaStored;
|
||||||
|
|
@ -2669,6 +2697,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
||||||
this.groupId = const Value.absent(),
|
this.groupId = const Value.absent(),
|
||||||
this.messageId = const Value.absent(),
|
this.messageId = const Value.absent(),
|
||||||
this.senderId = const Value.absent(),
|
this.senderId = const Value.absent(),
|
||||||
|
this.type = const Value.absent(),
|
||||||
this.content = const Value.absent(),
|
this.content = const Value.absent(),
|
||||||
this.mediaId = const Value.absent(),
|
this.mediaId = const Value.absent(),
|
||||||
this.mediaStored = const Value.absent(),
|
this.mediaStored = const Value.absent(),
|
||||||
|
|
@ -2683,6 +2712,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
||||||
required String groupId,
|
required String groupId,
|
||||||
this.messageId = const Value.absent(),
|
this.messageId = const Value.absent(),
|
||||||
this.senderId = const Value.absent(),
|
this.senderId = const Value.absent(),
|
||||||
|
required MessageType type,
|
||||||
this.content = const Value.absent(),
|
this.content = const Value.absent(),
|
||||||
this.mediaId = const Value.absent(),
|
this.mediaId = const Value.absent(),
|
||||||
this.mediaStored = const Value.absent(),
|
this.mediaStored = const Value.absent(),
|
||||||
|
|
@ -2692,11 +2722,13 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
||||||
this.isEdited = const Value.absent(),
|
this.isEdited = const Value.absent(),
|
||||||
this.createdAt = const Value.absent(),
|
this.createdAt = const Value.absent(),
|
||||||
this.rowid = const Value.absent(),
|
this.rowid = const Value.absent(),
|
||||||
}) : groupId = Value(groupId);
|
}) : groupId = Value(groupId),
|
||||||
|
type = Value(type);
|
||||||
static Insertable<Message> custom({
|
static Insertable<Message> custom({
|
||||||
Expression<String>? groupId,
|
Expression<String>? groupId,
|
||||||
Expression<String>? messageId,
|
Expression<String>? messageId,
|
||||||
Expression<int>? senderId,
|
Expression<int>? senderId,
|
||||||
|
Expression<String>? type,
|
||||||
Expression<String>? content,
|
Expression<String>? content,
|
||||||
Expression<String>? mediaId,
|
Expression<String>? mediaId,
|
||||||
Expression<bool>? mediaStored,
|
Expression<bool>? mediaStored,
|
||||||
|
|
@ -2711,6 +2743,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
||||||
if (groupId != null) 'group_id': groupId,
|
if (groupId != null) 'group_id': groupId,
|
||||||
if (messageId != null) 'message_id': messageId,
|
if (messageId != null) 'message_id': messageId,
|
||||||
if (senderId != null) 'sender_id': senderId,
|
if (senderId != null) 'sender_id': senderId,
|
||||||
|
if (type != null) 'type': type,
|
||||||
if (content != null) 'content': content,
|
if (content != null) 'content': content,
|
||||||
if (mediaId != null) 'media_id': mediaId,
|
if (mediaId != null) 'media_id': mediaId,
|
||||||
if (mediaStored != null) 'media_stored': mediaStored,
|
if (mediaStored != null) 'media_stored': mediaStored,
|
||||||
|
|
@ -2728,6 +2761,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
||||||
{Value<String>? groupId,
|
{Value<String>? groupId,
|
||||||
Value<String>? messageId,
|
Value<String>? messageId,
|
||||||
Value<int?>? senderId,
|
Value<int?>? senderId,
|
||||||
|
Value<MessageType>? type,
|
||||||
Value<String?>? content,
|
Value<String?>? content,
|
||||||
Value<String?>? mediaId,
|
Value<String?>? mediaId,
|
||||||
Value<bool>? mediaStored,
|
Value<bool>? mediaStored,
|
||||||
|
|
@ -2741,6 +2775,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
||||||
groupId: groupId ?? this.groupId,
|
groupId: groupId ?? this.groupId,
|
||||||
messageId: messageId ?? this.messageId,
|
messageId: messageId ?? this.messageId,
|
||||||
senderId: senderId ?? this.senderId,
|
senderId: senderId ?? this.senderId,
|
||||||
|
type: type ?? this.type,
|
||||||
content: content ?? this.content,
|
content: content ?? this.content,
|
||||||
mediaId: mediaId ?? this.mediaId,
|
mediaId: mediaId ?? this.mediaId,
|
||||||
mediaStored: mediaStored ?? this.mediaStored,
|
mediaStored: mediaStored ?? this.mediaStored,
|
||||||
|
|
@ -2765,6 +2800,10 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
||||||
if (senderId.present) {
|
if (senderId.present) {
|
||||||
map['sender_id'] = Variable<int>(senderId.value);
|
map['sender_id'] = Variable<int>(senderId.value);
|
||||||
}
|
}
|
||||||
|
if (type.present) {
|
||||||
|
map['type'] =
|
||||||
|
Variable<String>($MessagesTable.$convertertype.toSql(type.value));
|
||||||
|
}
|
||||||
if (content.present) {
|
if (content.present) {
|
||||||
map['content'] = Variable<String>(content.value);
|
map['content'] = Variable<String>(content.value);
|
||||||
}
|
}
|
||||||
|
|
@ -2801,6 +2840,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
||||||
..write('groupId: $groupId, ')
|
..write('groupId: $groupId, ')
|
||||||
..write('messageId: $messageId, ')
|
..write('messageId: $messageId, ')
|
||||||
..write('senderId: $senderId, ')
|
..write('senderId: $senderId, ')
|
||||||
|
..write('type: $type, ')
|
||||||
..write('content: $content, ')
|
..write('content: $content, ')
|
||||||
..write('mediaId: $mediaId, ')
|
..write('mediaId: $mediaId, ')
|
||||||
..write('mediaStored: $mediaStored, ')
|
..write('mediaStored: $mediaStored, ')
|
||||||
|
|
@ -6149,6 +6189,13 @@ abstract class _$TwonlyDB extends GeneratedDatabase {
|
||||||
TableUpdate('messages', kind: UpdateKind.delete),
|
TableUpdate('messages', kind: UpdateKind.delete),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
WritePropagation(
|
||||||
|
on: TableUpdateQuery.onTableName('media_files',
|
||||||
|
limitUpdateKind: UpdateKind.delete),
|
||||||
|
result: [
|
||||||
|
TableUpdate('messages', kind: UpdateKind.delete),
|
||||||
|
],
|
||||||
|
),
|
||||||
WritePropagation(
|
WritePropagation(
|
||||||
on: TableUpdateQuery.onTableName('messages',
|
on: TableUpdateQuery.onTableName('messages',
|
||||||
limitUpdateKind: UpdateKind.delete),
|
limitUpdateKind: UpdateKind.delete),
|
||||||
|
|
@ -7817,6 +7864,7 @@ typedef $$MessagesTableCreateCompanionBuilder = MessagesCompanion Function({
|
||||||
required String groupId,
|
required String groupId,
|
||||||
Value<String> messageId,
|
Value<String> messageId,
|
||||||
Value<int?> senderId,
|
Value<int?> senderId,
|
||||||
|
required MessageType type,
|
||||||
Value<String?> content,
|
Value<String?> content,
|
||||||
Value<String?> mediaId,
|
Value<String?> mediaId,
|
||||||
Value<bool> mediaStored,
|
Value<bool> mediaStored,
|
||||||
|
|
@ -7831,6 +7879,7 @@ typedef $$MessagesTableUpdateCompanionBuilder = MessagesCompanion Function({
|
||||||
Value<String> groupId,
|
Value<String> groupId,
|
||||||
Value<String> messageId,
|
Value<String> messageId,
|
||||||
Value<int?> senderId,
|
Value<int?> senderId,
|
||||||
|
Value<MessageType> type,
|
||||||
Value<String?> content,
|
Value<String?> content,
|
||||||
Value<String?> mediaId,
|
Value<String?> mediaId,
|
||||||
Value<bool> mediaStored,
|
Value<bool> mediaStored,
|
||||||
|
|
@ -7984,6 +8033,11 @@ class $$MessagesTableFilterComposer
|
||||||
ColumnFilters<String> get messageId => $composableBuilder(
|
ColumnFilters<String> get messageId => $composableBuilder(
|
||||||
column: $table.messageId, builder: (column) => ColumnFilters(column));
|
column: $table.messageId, builder: (column) => ColumnFilters(column));
|
||||||
|
|
||||||
|
ColumnWithTypeConverterFilters<MessageType, MessageType, String> get type =>
|
||||||
|
$composableBuilder(
|
||||||
|
column: $table.type,
|
||||||
|
builder: (column) => ColumnWithTypeConverterFilters(column));
|
||||||
|
|
||||||
ColumnFilters<String> get content => $composableBuilder(
|
ColumnFilters<String> get content => $composableBuilder(
|
||||||
column: $table.content, builder: (column) => ColumnFilters(column));
|
column: $table.content, builder: (column) => ColumnFilters(column));
|
||||||
|
|
||||||
|
|
@ -8180,6 +8234,9 @@ class $$MessagesTableOrderingComposer
|
||||||
ColumnOrderings<String> get messageId => $composableBuilder(
|
ColumnOrderings<String> get messageId => $composableBuilder(
|
||||||
column: $table.messageId, builder: (column) => ColumnOrderings(column));
|
column: $table.messageId, builder: (column) => ColumnOrderings(column));
|
||||||
|
|
||||||
|
ColumnOrderings<String> get type => $composableBuilder(
|
||||||
|
column: $table.type, builder: (column) => ColumnOrderings(column));
|
||||||
|
|
||||||
ColumnOrderings<String> get content => $composableBuilder(
|
ColumnOrderings<String> get content => $composableBuilder(
|
||||||
column: $table.content, builder: (column) => ColumnOrderings(column));
|
column: $table.content, builder: (column) => ColumnOrderings(column));
|
||||||
|
|
||||||
|
|
@ -8293,6 +8350,9 @@ class $$MessagesTableAnnotationComposer
|
||||||
GeneratedColumn<String> get messageId =>
|
GeneratedColumn<String> get messageId =>
|
||||||
$composableBuilder(column: $table.messageId, builder: (column) => column);
|
$composableBuilder(column: $table.messageId, builder: (column) => column);
|
||||||
|
|
||||||
|
GeneratedColumnWithTypeConverter<MessageType, String> get type =>
|
||||||
|
$composableBuilder(column: $table.type, builder: (column) => column);
|
||||||
|
|
||||||
GeneratedColumn<String> get content =>
|
GeneratedColumn<String> get content =>
|
||||||
$composableBuilder(column: $table.content, builder: (column) => column);
|
$composableBuilder(column: $table.content, builder: (column) => column);
|
||||||
|
|
||||||
|
|
@ -8510,6 +8570,7 @@ class $$MessagesTableTableManager extends RootTableManager<
|
||||||
Value<String> groupId = const Value.absent(),
|
Value<String> groupId = const Value.absent(),
|
||||||
Value<String> messageId = const Value.absent(),
|
Value<String> messageId = const Value.absent(),
|
||||||
Value<int?> senderId = const Value.absent(),
|
Value<int?> senderId = const Value.absent(),
|
||||||
|
Value<MessageType> type = const Value.absent(),
|
||||||
Value<String?> content = const Value.absent(),
|
Value<String?> content = const Value.absent(),
|
||||||
Value<String?> mediaId = const Value.absent(),
|
Value<String?> mediaId = const Value.absent(),
|
||||||
Value<bool> mediaStored = const Value.absent(),
|
Value<bool> mediaStored = const Value.absent(),
|
||||||
|
|
@ -8524,6 +8585,7 @@ class $$MessagesTableTableManager extends RootTableManager<
|
||||||
groupId: groupId,
|
groupId: groupId,
|
||||||
messageId: messageId,
|
messageId: messageId,
|
||||||
senderId: senderId,
|
senderId: senderId,
|
||||||
|
type: type,
|
||||||
content: content,
|
content: content,
|
||||||
mediaId: mediaId,
|
mediaId: mediaId,
|
||||||
mediaStored: mediaStored,
|
mediaStored: mediaStored,
|
||||||
|
|
@ -8538,6 +8600,7 @@ class $$MessagesTableTableManager extends RootTableManager<
|
||||||
required String groupId,
|
required String groupId,
|
||||||
Value<String> messageId = const Value.absent(),
|
Value<String> messageId = const Value.absent(),
|
||||||
Value<int?> senderId = const Value.absent(),
|
Value<int?> senderId = const Value.absent(),
|
||||||
|
required MessageType type,
|
||||||
Value<String?> content = const Value.absent(),
|
Value<String?> content = const Value.absent(),
|
||||||
Value<String?> mediaId = const Value.absent(),
|
Value<String?> mediaId = const Value.absent(),
|
||||||
Value<bool> mediaStored = const Value.absent(),
|
Value<bool> mediaStored = const Value.absent(),
|
||||||
|
|
@ -8552,6 +8615,7 @@ class $$MessagesTableTableManager extends RootTableManager<
|
||||||
groupId: groupId,
|
groupId: groupId,
|
||||||
messageId: messageId,
|
messageId: messageId,
|
||||||
senderId: senderId,
|
senderId: senderId,
|
||||||
|
type: type,
|
||||||
content: content,
|
content: content,
|
||||||
mediaId: mediaId,
|
mediaId: mediaId,
|
||||||
mediaStored: mediaStored,
|
mediaStored: mediaStored,
|
||||||
|
|
|
||||||
|
|
@ -1,88 +1,30 @@
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
import 'package:twonly/globals.dart';
|
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/json/message_old.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/upload.service.dart' as send;
|
|
||||||
import 'package:twonly/src/services/mediafiles/thumbnail.service.dart';
|
|
||||||
|
|
||||||
class MemoryItem {
|
class MemoryItem {
|
||||||
MemoryItem({
|
MemoryItem({
|
||||||
required this.id,
|
required this.mediaService,
|
||||||
required this.messages,
|
required this.messages,
|
||||||
required this.date,
|
|
||||||
required this.mirrorVideo,
|
|
||||||
required this.thumbnailPath,
|
|
||||||
this.imagePath,
|
|
||||||
this.videoPath,
|
|
||||||
});
|
});
|
||||||
final int id;
|
|
||||||
final bool mirrorVideo;
|
|
||||||
final List<Message> messages;
|
final List<Message> messages;
|
||||||
final DateTime date;
|
final MediaFileService mediaService;
|
||||||
final File thumbnailPath;
|
|
||||||
final File? imagePath;
|
|
||||||
final File? videoPath;
|
|
||||||
|
|
||||||
static Future<Map<int, MemoryItem>> convertFromMessages(
|
static Future<Map<String, MemoryItem>> convertFromMessages(
|
||||||
List<Message> messages,
|
List<Message> messages,
|
||||||
) async {
|
) async {
|
||||||
final items = <int, MemoryItem>{};
|
final items = <String, MemoryItem>{};
|
||||||
for (final message in messages) {
|
for (final message in messages) {
|
||||||
final isSend = message.messageOtherId == null;
|
if (message.mediaId == null) continue;
|
||||||
final id = message.mediaUploadId ?? message.messageId;
|
|
||||||
final basePath = await send.getMediaFilePath(
|
final mediaService = await MediaFileService.fromMediaId(message.mediaId!);
|
||||||
id,
|
if (mediaService == null) continue;
|
||||||
isSend ? 'send' : 'received',
|
|
||||||
);
|
|
||||||
File? imagePath;
|
|
||||||
late File thumbnailFile;
|
|
||||||
File? videoPath;
|
|
||||||
if (File('$basePath.mp4').existsSync()) {
|
|
||||||
videoPath = File('$basePath.mp4');
|
|
||||||
thumbnailFile = getThumbnailPath(videoPath);
|
|
||||||
if (!thumbnailFile.existsSync()) {
|
|
||||||
await createThumbnailsForVideo(videoPath);
|
|
||||||
}
|
|
||||||
} else if (File('$basePath.png').existsSync()) {
|
|
||||||
imagePath = File('$basePath.png');
|
|
||||||
thumbnailFile = getThumbnailPath(imagePath);
|
|
||||||
if (!thumbnailFile.existsSync()) {
|
|
||||||
await createThumbnailsForImage(imagePath);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (message.mediaStored) {
|
|
||||||
/// media file was deleted, ... remove the file
|
|
||||||
await twonlyDB.messagesDao.updateMessageByMessageId(
|
|
||||||
message.messageId,
|
|
||||||
const MessagesCompanion(
|
|
||||||
mediaStored: Value(false),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var mirrorVideo = false;
|
|
||||||
if (videoPath != null) {
|
|
||||||
final content = MediaMessageContent.fromJson(
|
|
||||||
jsonDecode(message.contentJson!) as Map,
|
|
||||||
);
|
|
||||||
mirrorVideo = content.mirrorVideo;
|
|
||||||
}
|
|
||||||
|
|
||||||
items
|
items
|
||||||
.putIfAbsent(
|
.putIfAbsent(
|
||||||
id,
|
message.mediaId!,
|
||||||
() => MemoryItem(
|
() => MemoryItem(
|
||||||
id: id,
|
mediaService: mediaService,
|
||||||
messages: [],
|
messages: [],
|
||||||
date: message.sendAt,
|
|
||||||
mirrorVideo: mirrorVideo,
|
|
||||||
thumbnailPath: thumbnailFile,
|
|
||||||
imagePath: imagePath,
|
|
||||||
videoPath: videoPath,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.messages
|
.messages
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||||
import 'package:gal/gal.dart';
|
import 'package:gal/gal.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:local_auth/local_auth.dart';
|
import 'package:local_auth/local_auth.dart';
|
||||||
|
import 'package:pie_menu/pie_menu.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/localization/generated/app_localizations.dart';
|
import 'package:twonly/src/localization/generated/app_localizations.dart';
|
||||||
|
|
@ -258,3 +259,28 @@ Uint8List hexToUint8List(String hex) => Uint8List.fromList(
|
||||||
(i) => int.parse(hex.substring(i * 2, i * 2 + 2), radix: 16),
|
(i) => int.parse(hex.substring(i * 2, i * 2 + 2), radix: 16),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
PieTheme getPieCanvasTheme(BuildContext context) {
|
||||||
|
return PieTheme(
|
||||||
|
brightness: Theme.of(context).brightness,
|
||||||
|
rightClickShowsMenu: true,
|
||||||
|
radius: 70,
|
||||||
|
buttonTheme: PieButtonTheme(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.tertiary,
|
||||||
|
iconColor: Theme.of(context).colorScheme.surfaceBright,
|
||||||
|
),
|
||||||
|
buttonThemeHovered: PieButtonTheme(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
|
iconColor: Theme.of(context).colorScheme.surfaceBright,
|
||||||
|
),
|
||||||
|
tooltipPadding: const EdgeInsets.all(20),
|
||||||
|
overlayColor: isDarkMode(context)
|
||||||
|
? const Color.fromARGB(69, 0, 0, 0)
|
||||||
|
: const Color.fromARGB(40, 0, 0, 0),
|
||||||
|
// spacing: 0,
|
||||||
|
tooltipTextStyle: const TextStyle(
|
||||||
|
fontSize: 32,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,8 +71,14 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
selectedGroupIds.add(widget.sendToGroup!.groupId);
|
selectedGroupIds.add(widget.sendToGroup!.groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (widget.mediaFileService.mediaFile.type == MediaType.image) {
|
||||||
if (widget.imageBytesFuture != null) {
|
if (widget.imageBytesFuture != null) {
|
||||||
unawaited(loadImage(widget.imageBytesFuture!));
|
loadImage(widget.imageBytesFuture!);
|
||||||
|
} else {
|
||||||
|
if (widget.mediaFileService.storedPath.existsSync()) {
|
||||||
|
loadImage(widget.mediaFileService.storedPath.readAsBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (media.type == MediaType.video) {
|
if (media.type == MediaType.video) {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/json/userdata.dart';
|
import 'package:twonly/src/model/json/userdata.dart';
|
||||||
import 'package:twonly/src/providers/connection.provider.dart';
|
import 'package:twonly/src/providers/connection.provider.dart';
|
||||||
|
|
@ -28,7 +27,7 @@ import 'package:twonly/src/views/chats/start_new_chat.view.dart';
|
||||||
import 'package:twonly/src/views/components/flame.dart';
|
import 'package:twonly/src/views/components/flame.dart';
|
||||||
import 'package:twonly/src/views/components/initialsavatar.dart';
|
import 'package:twonly/src/views/components/initialsavatar.dart';
|
||||||
import 'package:twonly/src/views/components/notification_badge.dart';
|
import 'package:twonly/src/views/components/notification_badge.dart';
|
||||||
import 'package:twonly/src/views/components/user_context_menu.dart';
|
import 'package:twonly/src/views/components/user_context_menu.component.dart';
|
||||||
import 'package:twonly/src/views/settings/help/changelog.view.dart';
|
import 'package:twonly/src/views/settings/help/changelog.view.dart';
|
||||||
import 'package:twonly/src/views/settings/profile/profile.view.dart';
|
import 'package:twonly/src/views/settings/profile/profile.view.dart';
|
||||||
import 'package:twonly/src/views/settings/settings_main.view.dart';
|
import 'package:twonly/src/views/settings/settings_main.view.dart';
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import 'package:twonly/src/views/chats/chat_messages_components/chat_list_entry.
|
||||||
import 'package:twonly/src/views/chats/chat_messages_components/response_container.dart';
|
import 'package:twonly/src/views/chats/chat_messages_components/response_container.dart';
|
||||||
import 'package:twonly/src/views/components/animate_icon.dart';
|
import 'package:twonly/src/views/components/animate_icon.dart';
|
||||||
import 'package:twonly/src/views/components/initialsavatar.dart';
|
import 'package:twonly/src/views/components/initialsavatar.dart';
|
||||||
import 'package:twonly/src/views/components/user_context_menu.dart';
|
import 'package:twonly/src/views/components/user_context_menu.component.dart';
|
||||||
import 'package:twonly/src/views/components/verified_shield.dart';
|
import 'package:twonly/src/views/components/verified_shield.dart';
|
||||||
import 'package:twonly/src/views/contact/contact.view.dart';
|
import 'package:twonly/src/views/contact/contact.view.dart';
|
||||||
import 'package:twonly/src/views/tutorial/tutorials.dart';
|
import 'package:twonly/src/views/tutorial/tutorials.dart';
|
||||||
|
|
@ -61,9 +61,9 @@ class ChatItem {
|
||||||
|
|
||||||
/// Displays detailed information about a SampleItem.
|
/// Displays detailed information about a SampleItem.
|
||||||
class ChatMessagesView extends StatefulWidget {
|
class ChatMessagesView extends StatefulWidget {
|
||||||
const ChatMessagesView(this.contact, {super.key});
|
const ChatMessagesView(this.group, {super.key});
|
||||||
|
|
||||||
final Contact contact;
|
final Group group;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ChatMessagesView> createState() => _ChatMessagesViewState();
|
State<ChatMessagesView> createState() => _ChatMessagesViewState();
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import 'package:twonly/src/views/chats/add_new_user.view.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages.view.dart';
|
import 'package:twonly/src/views/chats/chat_messages.view.dart';
|
||||||
import 'package:twonly/src/views/components/flame.dart';
|
import 'package:twonly/src/views/components/flame.dart';
|
||||||
import 'package:twonly/src/views/components/initialsavatar.dart';
|
import 'package:twonly/src/views/components/initialsavatar.dart';
|
||||||
import 'package:twonly/src/views/components/user_context_menu.dart';
|
import 'package:twonly/src/views/components/user_context_menu.component.dart';
|
||||||
|
|
||||||
class StartNewChatView extends StatefulWidget {
|
class StartNewChatView extends StatefulWidget {
|
||||||
const StartNewChatView({super.key});
|
const StartNewChatView({super.key});
|
||||||
|
|
|
||||||
96
lib/src/views/components/group_context_menu.component.dart
Normal file
96
lib/src/views/components/group_context_menu.component.dart
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:pie_menu/pie_menu.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/views/chats/chat_messages.view.dart';
|
||||||
|
|
||||||
|
class GroupContextMenu extends StatefulWidget {
|
||||||
|
const GroupContextMenu({
|
||||||
|
required this.group,
|
||||||
|
required this.child,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
final Widget child;
|
||||||
|
final Group group;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<GroupContextMenu> createState() => _GroupContextMenuState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _GroupContextMenuState extends State<GroupContextMenu> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return PieMenu(
|
||||||
|
onPressed: () => (),
|
||||||
|
onToggle: (menuOpen) async {
|
||||||
|
if (menuOpen) {
|
||||||
|
await HapticFeedback.heavyImpact();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: [
|
||||||
|
if (!widget.group.archived)
|
||||||
|
PieAction(
|
||||||
|
tooltip: Text(context.lang.contextMenuArchiveUser),
|
||||||
|
onSelect: () async {
|
||||||
|
const update = GroupsCompanion(archived: Value(true));
|
||||||
|
if (context.mounted) {
|
||||||
|
await twonlyDB.groupsDao
|
||||||
|
.updateGroup(widget.group.groupId, update);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const FaIcon(FontAwesomeIcons.boxArchive),
|
||||||
|
),
|
||||||
|
if (widget.group.archived)
|
||||||
|
PieAction(
|
||||||
|
tooltip: Text(context.lang.contextMenuUndoArchiveUser),
|
||||||
|
onSelect: () async {
|
||||||
|
const update = GroupsCompanion(archived: Value(false));
|
||||||
|
if (context.mounted) {
|
||||||
|
await twonlyDB.groupsDao
|
||||||
|
.updateGroup(widget.group.groupId, update);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const FaIcon(FontAwesomeIcons.boxOpen),
|
||||||
|
),
|
||||||
|
PieAction(
|
||||||
|
tooltip: Text(context.lang.contextMenuOpenChat),
|
||||||
|
onSelect: () async {
|
||||||
|
await Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) {
|
||||||
|
return ChatMessagesView(widget.group);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const FaIcon(FontAwesomeIcons.solidComments),
|
||||||
|
),
|
||||||
|
PieAction(
|
||||||
|
tooltip: Text(
|
||||||
|
widget.group.pinned
|
||||||
|
? context.lang.contextMenuUnpin
|
||||||
|
: context.lang.contextMenuPin,
|
||||||
|
),
|
||||||
|
onSelect: () async {
|
||||||
|
final update = GroupsCompanion(pinned: Value(!widget.group.pinned));
|
||||||
|
if (context.mounted) {
|
||||||
|
await twonlyDB.groupsDao
|
||||||
|
.updateGroup(widget.group.groupId, update);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: FaIcon(
|
||||||
|
widget.group.pinned
|
||||||
|
? FontAwesomeIcons.thumbtackSlash
|
||||||
|
: FontAwesomeIcons.thumbtack,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: widget.child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
45
lib/src/views/components/user_context_menu.component.dart
Normal file
45
lib/src/views/components/user_context_menu.component.dart
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:pie_menu/pie_menu.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/views/contact/contact.view.dart';
|
||||||
|
|
||||||
|
class UserContextMenuBlocked extends StatefulWidget {
|
||||||
|
const UserContextMenuBlocked({
|
||||||
|
required this.contact,
|
||||||
|
required this.child,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
final Widget child;
|
||||||
|
final Contact contact;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<UserContextMenuBlocked> createState() => _UserContextMenuBlocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _UserContextMenuBlocked extends State<UserContextMenuBlocked> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return PieMenu(
|
||||||
|
onPressed: () => (),
|
||||||
|
actions: [
|
||||||
|
PieAction(
|
||||||
|
tooltip: Text(context.lang.contextMenuUserProfile),
|
||||||
|
onSelect: () async {
|
||||||
|
await Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) {
|
||||||
|
return ContactView(widget.contact.userId);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const FaIcon(FontAwesomeIcons.user),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: widget.child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
||||||
import 'package:pie_menu/pie_menu.dart';
|
|
||||||
import 'package:twonly/globals.dart';
|
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
|
||||||
import 'package:twonly/src/views/chats/chat_messages.view.dart';
|
|
||||||
import 'package:twonly/src/views/contact/contact.view.dart';
|
|
||||||
|
|
||||||
class UserContextMenu extends StatefulWidget {
|
|
||||||
const UserContextMenu({
|
|
||||||
required this.contact,
|
|
||||||
required this.child,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
final Widget child;
|
|
||||||
final Contact contact;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<UserContextMenu> createState() => _UserContextMenuState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _UserContextMenuState extends State<UserContextMenu> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return PieMenu(
|
|
||||||
onPressed: () => (),
|
|
||||||
onToggle: (menuOpen) async {
|
|
||||||
if (menuOpen) {
|
|
||||||
await HapticFeedback.heavyImpact();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: [
|
|
||||||
if (!widget.contact.archived)
|
|
||||||
PieAction(
|
|
||||||
tooltip: Text(context.lang.contextMenuArchiveUser),
|
|
||||||
onSelect: () async {
|
|
||||||
const update = ContactsCompanion(archived: Value(true));
|
|
||||||
if (context.mounted) {
|
|
||||||
await twonlyDB.contactsDao
|
|
||||||
.updateContact(widget.contact.userId, update);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: const FaIcon(FontAwesomeIcons.boxArchive),
|
|
||||||
),
|
|
||||||
if (widget.contact.archived)
|
|
||||||
PieAction(
|
|
||||||
tooltip: Text(context.lang.contextMenuUndoArchiveUser),
|
|
||||||
onSelect: () async {
|
|
||||||
const update = ContactsCompanion(archived: Value(false));
|
|
||||||
if (context.mounted) {
|
|
||||||
await twonlyDB.contactsDao
|
|
||||||
.updateContact(widget.contact.userId, update);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: const FaIcon(FontAwesomeIcons.boxOpen),
|
|
||||||
),
|
|
||||||
PieAction(
|
|
||||||
tooltip: Text(context.lang.contextMenuOpenChat),
|
|
||||||
onSelect: () async {
|
|
||||||
await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return ChatMessagesView(widget.contact);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: const FaIcon(FontAwesomeIcons.solidComments),
|
|
||||||
),
|
|
||||||
PieAction(
|
|
||||||
tooltip: Text(
|
|
||||||
widget.contact.pinned
|
|
||||||
? context.lang.contextMenuUnpin
|
|
||||||
: context.lang.contextMenuPin,
|
|
||||||
),
|
|
||||||
onSelect: () async {
|
|
||||||
final update =
|
|
||||||
ContactsCompanion(pinned: Value(!widget.contact.pinned));
|
|
||||||
if (context.mounted) {
|
|
||||||
await twonlyDB.contactsDao
|
|
||||||
.updateContact(widget.contact.userId, update);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: FaIcon(
|
|
||||||
widget.contact.pinned
|
|
||||||
? FontAwesomeIcons.thumbtackSlash
|
|
||||||
: FontAwesomeIcons.thumbtack,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: widget.child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UserContextMenuBlocked extends StatefulWidget {
|
|
||||||
const UserContextMenuBlocked({
|
|
||||||
required this.contact,
|
|
||||||
required this.child,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
final Widget child;
|
|
||||||
final Contact contact;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<UserContextMenuBlocked> createState() => _UserContextMenuBlocked();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _UserContextMenuBlocked extends State<UserContextMenuBlocked> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return PieMenu(
|
|
||||||
onPressed: () => (),
|
|
||||||
actions: [
|
|
||||||
if (!widget.contact.archived)
|
|
||||||
PieAction(
|
|
||||||
tooltip: Text(context.lang.contextMenuArchiveUser),
|
|
||||||
onSelect: () async {
|
|
||||||
const update = ContactsCompanion(archived: Value(true));
|
|
||||||
if (context.mounted) {
|
|
||||||
await twonlyDB.contactsDao
|
|
||||||
.updateContact(widget.contact.userId, update);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: const FaIcon(FontAwesomeIcons.boxArchive),
|
|
||||||
),
|
|
||||||
if (widget.contact.archived)
|
|
||||||
PieAction(
|
|
||||||
tooltip: Text(context.lang.contextMenuUndoArchiveUser),
|
|
||||||
onSelect: () async {
|
|
||||||
const update = ContactsCompanion(archived: Value(false));
|
|
||||||
if (context.mounted) {
|
|
||||||
await twonlyDB.contactsDao
|
|
||||||
.updateContact(widget.contact.userId, update);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: const FaIcon(FontAwesomeIcons.boxOpen),
|
|
||||||
),
|
|
||||||
PieAction(
|
|
||||||
tooltip: Text(context.lang.contextMenuUserProfile),
|
|
||||||
onSelect: () async {
|
|
||||||
await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return ContactView(widget.contact.userId);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: const FaIcon(FontAwesomeIcons.user),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: widget.child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PieTheme getPieCanvasTheme(BuildContext context) {
|
|
||||||
return PieTheme(
|
|
||||||
brightness: Theme.of(context).brightness,
|
|
||||||
rightClickShowsMenu: true,
|
|
||||||
radius: 70,
|
|
||||||
buttonTheme: PieButtonTheme(
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.tertiary,
|
|
||||||
iconColor: Theme.of(context).colorScheme.surfaceBright,
|
|
||||||
),
|
|
||||||
buttonThemeHovered: PieButtonTheme(
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
||||||
iconColor: Theme.of(context).colorScheme.surfaceBright,
|
|
||||||
),
|
|
||||||
tooltipPadding: const EdgeInsets.all(20),
|
|
||||||
overlayColor: isDarkMode(context)
|
|
||||||
? const Color.fromARGB(69, 0, 0, 0)
|
|
||||||
: const Color.fromARGB(40, 0, 0, 0),
|
|
||||||
// spacing: 0,
|
|
||||||
tooltipTextStyle: const TextStyle(
|
|
||||||
fontSize: 32,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -7,11 +7,9 @@ import 'package:video_player/video_player.dart';
|
||||||
class VideoPlayerWrapper extends StatefulWidget {
|
class VideoPlayerWrapper extends StatefulWidget {
|
||||||
const VideoPlayerWrapper({
|
const VideoPlayerWrapper({
|
||||||
required this.videoPath,
|
required this.videoPath,
|
||||||
required this.mirrorVideo,
|
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
final File videoPath;
|
final File videoPath;
|
||||||
final bool mirrorVideo;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<VideoPlayerWrapper> createState() => _VideoPlayerWrapperState();
|
State<VideoPlayerWrapper> createState() => _VideoPlayerWrapperState();
|
||||||
|
|
@ -48,10 +46,7 @@ class _VideoPlayerWrapperState extends State<VideoPlayerWrapper> {
|
||||||
child: _controller.value.isInitialized
|
child: _controller.value.isInitialized
|
||||||
? AspectRatio(
|
? AspectRatio(
|
||||||
aspectRatio: _controller.value.aspectRatio,
|
aspectRatio: _controller.value.aspectRatio,
|
||||||
child: Transform.flip(
|
|
||||||
flipX: widget.mirrorVideo,
|
|
||||||
child: VideoPlayer(_controller),
|
child: VideoPlayer(_controller),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
: const CircularProgressIndicator(), // Show loading indicator while initializing
|
: const CircularProgressIndicator(), // Show loading indicator while initializing
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -163,26 +163,26 @@ class _ContactViewState extends State<ContactView> {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
BetterListTile(
|
// BetterListTile(
|
||||||
icon: FontAwesomeIcons.eraser,
|
// icon: FontAwesomeIcons.eraser,
|
||||||
iconSize: 16,
|
// iconSize: 16,
|
||||||
text: context.lang.deleteAllContactMessages,
|
// text: context.lang.deleteAllContactMessages,
|
||||||
onTap: () async {
|
// onTap: () async {
|
||||||
final block = await showAlertDialog(
|
// final block = await showAlertDialog(
|
||||||
context,
|
// context,
|
||||||
context.lang.deleteAllContactMessages,
|
// context.lang.deleteAllContactMessages,
|
||||||
context.lang.deleteAllContactMessagesBody(
|
// context.lang.deleteAllContactMessagesBody(
|
||||||
getContactDisplayName(contact),
|
// getContactDisplayName(contact),
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
if (block) {
|
// if (block) {
|
||||||
if (context.mounted) {
|
// if (context.mounted) {
|
||||||
await twonlyDB.messagesDao
|
// await twonlyDB.messagesDao
|
||||||
.deleteMessagesByContactId(contact.userId);
|
// .deleteMessagesByContactId(contact.userId);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
),
|
// ),
|
||||||
BetterListTile(
|
BetterListTile(
|
||||||
icon: FontAwesomeIcons.flag,
|
icon: FontAwesomeIcons.flag,
|
||||||
text: context.lang.reportUser,
|
text: context.lang.reportUser,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_controller_view.dart';
|
import 'package:twonly/src/views/camera/camera_preview_controller_view.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_list.view.dart';
|
import 'package:twonly/src/views/chats/chat_list.view.dart';
|
||||||
import 'package:twonly/src/views/components/user_context_menu.dart';
|
|
||||||
import 'package:twonly/src/views/memories/memories.view.dart';
|
import 'package:twonly/src/views/memories/memories.view.dart';
|
||||||
|
|
||||||
void Function(int) globalUpdateOfHomeViewPageIndex = (a) {};
|
void Function(int) globalUpdateOfHomeViewPageIndex = (a) {};
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/memory_item.model.dart';
|
import 'package:twonly/src/model/memory_item.model.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/upload.service.dart' as send;
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/thumbnail.service.dart';
|
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/memories/memories_item_thumbnail.dart';
|
import 'package:twonly/src/views/memories/memories_item_thumbnail.dart';
|
||||||
import 'package:twonly/src/views/memories/memories_photo_slider.view.dart';
|
import 'package:twonly/src/views/memories/memories_photo_slider.view.dart';
|
||||||
|
|
@ -24,7 +22,7 @@ class MemoriesViewState extends State<MemoriesView> {
|
||||||
List<MemoryItem> galleryItems = [];
|
List<MemoryItem> galleryItems = [];
|
||||||
Map<String, List<int>> orderedByMonth = {};
|
Map<String, List<int>> orderedByMonth = {};
|
||||||
List<String> months = [];
|
List<String> months = [];
|
||||||
StreamSubscription<List<Message>>? messageSub;
|
StreamSubscription<List<MediaFile>>? messageSub;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -38,76 +36,37 @@ class MemoriesViewState extends State<MemoriesView> {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<MemoryItem>> loadMemoriesDirectory() async {
|
|
||||||
final directoryPath = await send.getMediaBaseFilePath('memories');
|
|
||||||
final directory = Directory(directoryPath);
|
|
||||||
|
|
||||||
final items = <MemoryItem>[];
|
|
||||||
if (directory.existsSync()) {
|
|
||||||
final files = directory.listSync();
|
|
||||||
|
|
||||||
for (final file in files) {
|
|
||||||
if (file is File) {
|
|
||||||
final fileName = file.uri.pathSegments.last;
|
|
||||||
File? imagePath;
|
|
||||||
File? videoPath;
|
|
||||||
late File thumbnailFile;
|
|
||||||
if (fileName.contains('.thumbnail.')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (fileName.contains('.png')) {
|
|
||||||
imagePath = file;
|
|
||||||
thumbnailFile = file;
|
|
||||||
// if (!await thumbnailFile.exists()) {
|
|
||||||
// await createThumbnailsForImage(imagePath);
|
|
||||||
// }
|
|
||||||
} else if (fileName.contains('.mp4')) {
|
|
||||||
videoPath = file;
|
|
||||||
thumbnailFile = getThumbnailPath(videoPath);
|
|
||||||
if (!thumbnailFile.existsSync()) {
|
|
||||||
await createThumbnailsForVideo(videoPath);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
final creationDate = file.lastModifiedSync();
|
|
||||||
items.add(
|
|
||||||
MemoryItem(
|
|
||||||
id: int.parse(fileName.split('.')[0]),
|
|
||||||
messages: [],
|
|
||||||
date: creationDate,
|
|
||||||
mirrorVideo: false,
|
|
||||||
thumbnailPath: thumbnailFile,
|
|
||||||
imagePath: imagePath,
|
|
||||||
videoPath: videoPath,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> initAsync() async {
|
Future<void> initAsync() async {
|
||||||
await messageSub?.cancel();
|
await messageSub?.cancel();
|
||||||
final msgStream = twonlyDB.messagesDao.getAllStoredMediaFiles();
|
final msgStream = twonlyDB.mediaFilesDao.watchAllStoredMediaFiles();
|
||||||
|
|
||||||
messageSub = msgStream.listen((msgs) async {
|
messageSub = msgStream.listen((mediaFiles) async {
|
||||||
final items = await MemoryItem.convertFromMessages(msgs);
|
|
||||||
// Group items by month
|
// Group items by month
|
||||||
orderedByMonth = {};
|
orderedByMonth = {};
|
||||||
months = [];
|
months = [];
|
||||||
var lastMonth = '';
|
var lastMonth = '';
|
||||||
galleryItems = await loadMemoriesDirectory();
|
galleryItems = [];
|
||||||
for (final item in galleryItems) {
|
final applicationSupportDirectory =
|
||||||
items.remove(
|
await getApplicationSupportDirectory();
|
||||||
item.id,
|
for (final mediaFile in mediaFiles) {
|
||||||
); // prefer the stored one and not the saved on in the chat....
|
galleryItems.add(
|
||||||
|
MemoryItem(
|
||||||
|
mediaService: MediaFileService(
|
||||||
|
mediaFile,
|
||||||
|
applicationSupportDirectory: applicationSupportDirectory,
|
||||||
|
),
|
||||||
|
messages: [],
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
galleryItems += items.values.toList();
|
galleryItems.sort(
|
||||||
galleryItems.sort((a, b) => b.date.compareTo(a.date));
|
(a, b) => b.mediaService.mediaFile.createdAt.compareTo(
|
||||||
|
a.mediaService.mediaFile.createdAt,
|
||||||
|
),
|
||||||
|
);
|
||||||
for (var i = 0; i < galleryItems.length; i++) {
|
for (var i = 0; i < galleryItems.length; i++) {
|
||||||
final month = DateFormat('MMMM yyyy').format(galleryItems[i].date);
|
final month = DateFormat('MMMM yyyy')
|
||||||
|
.format(galleryItems[i].mediaService.mediaFile.createdAt);
|
||||||
if (lastMonth != month) {
|
if (lastMonth != month) {
|
||||||
lastMonth = month;
|
lastMonth = month;
|
||||||
months.add(month);
|
months.add(month);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
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:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/model/memory_item.model.dart';
|
import 'package:twonly/src/model/memory_item.model.dart';
|
||||||
|
|
||||||
class MemoriesItemThumbnail extends StatefulWidget {
|
class MemoriesItemThumbnail extends StatefulWidget {
|
||||||
|
|
@ -39,11 +40,12 @@ class _MemoriesItemThumbnailState extends State<MemoriesItemThumbnail> {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: widget.onTap,
|
onTap: widget.onTap,
|
||||||
child: Hero(
|
child: Hero(
|
||||||
tag: widget.galleryItem.id.toString(),
|
tag: widget.galleryItem.mediaService.mediaFile.mediaId,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Image.file(widget.galleryItem.thumbnailPath),
|
Image.file(widget.galleryItem.mediaService.thumbnailPath),
|
||||||
if (widget.galleryItem.videoPath != null)
|
if (widget.galleryItem.mediaService.mediaFile.type ==
|
||||||
|
MediaType.video)
|
||||||
const Positioned.fill(
|
const Positioned.fill(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: FaIcon(FontAwesomeIcons.circlePlay),
|
child: FaIcon(FontAwesomeIcons.circlePlay),
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
import 'package:drift/drift.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:photo_view/photo_view.dart';
|
import 'package:photo_view/photo_view.dart';
|
||||||
import 'package:photo_view/photo_view_gallery.dart';
|
import 'package:photo_view/photo_view_gallery.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/model/memory_item.model.dart';
|
import 'package:twonly/src/model/memory_item.model.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/download.service.dart'
|
|
||||||
as received;
|
|
||||||
import 'package:twonly/src/services/api/mediafiles/upload.service.dart' as send;
|
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
||||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||||
|
|
@ -51,7 +47,6 @@ class _MemoriesPhotoSliderViewState extends State<MemoriesPhotoSliderView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteFile() async {
|
Future<void> deleteFile() async {
|
||||||
final messages = widget.galleryItems[currentIndex].messages;
|
|
||||||
final confirmed = await showAlertDialog(
|
final confirmed = await showAlertDialog(
|
||||||
context,
|
context,
|
||||||
context.lang.deleteImageTitle,
|
context.lang.deleteImageTitle,
|
||||||
|
|
@ -60,32 +55,26 @@ class _MemoriesPhotoSliderViewState extends State<MemoriesPhotoSliderView> {
|
||||||
|
|
||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
|
|
||||||
widget.galleryItems[currentIndex].imagePath?.deleteSync();
|
widget.galleryItems[currentIndex].mediaService.fullMediaRemoval();
|
||||||
widget.galleryItems[currentIndex].videoPath?.deleteSync();
|
await twonlyDB.mediaFilesDao.deleteMediaFile(
|
||||||
for (final message in messages) {
|
widget.galleryItems[currentIndex].mediaService.mediaFile.mediaId,
|
||||||
await twonlyDB.messagesDao.updateMessageByMessageId(
|
|
||||||
message.messageId,
|
|
||||||
const MessagesCompanion(mediaStored: Value(false)),
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
widget.galleryItems.removeAt(currentIndex);
|
widget.galleryItems.removeAt(currentIndex);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
await send.purgeSendMediaFiles();
|
|
||||||
await received.purgeReceivedMediaFiles();
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
Navigator.pop(context, true);
|
Navigator.pop(context, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> exportFile() async {
|
Future<void> exportFile() async {
|
||||||
final item = widget.galleryItems[currentIndex];
|
final item = widget.galleryItems[currentIndex].mediaService;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (item.videoPath != null) {
|
if (item.mediaFile.type == MediaType.video) {
|
||||||
await saveVideoToGallery(item.videoPath!.path);
|
await saveVideoToGallery(item.storedPath.path);
|
||||||
} else if (item.imagePath != null) {
|
} else if (item.mediaFile.type == MediaType.image) {
|
||||||
final imageBytes = await item.imagePath!.readAsBytes();
|
final imageBytes = await item.storedPath.readAsBytes();
|
||||||
await saveImageToGallery(imageBytes);
|
await saveImageToGallery(imageBytes);
|
||||||
}
|
}
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
@ -132,14 +121,9 @@ class _MemoriesPhotoSliderViewState extends State<MemoriesPhotoSliderView> {
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => ShareImageEditorView(
|
builder: (context) => ShareImageEditorView(
|
||||||
videoFilePath:
|
mediaFileService: widget
|
||||||
widget.galleryItems[currentIndex].videoPath,
|
.galleryItems[currentIndex].mediaService,
|
||||||
imageBytes: widget
|
|
||||||
.galleryItems[currentIndex].imagePath
|
|
||||||
?.readAsBytes(),
|
|
||||||
mirrorVideo: false,
|
|
||||||
sharedFromGallery: true,
|
sharedFromGallery: true,
|
||||||
useHighQuality: true,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -214,24 +198,25 @@ class _MemoriesPhotoSliderViewState extends State<MemoriesPhotoSliderView> {
|
||||||
|
|
||||||
PhotoViewGalleryPageOptions _buildItem(BuildContext context, int index) {
|
PhotoViewGalleryPageOptions _buildItem(BuildContext context, int index) {
|
||||||
final item = widget.galleryItems[index];
|
final item = widget.galleryItems[index];
|
||||||
return item.videoPath != null
|
return item.mediaService.mediaFile.type == MediaType.video
|
||||||
? PhotoViewGalleryPageOptions.customChild(
|
? PhotoViewGalleryPageOptions.customChild(
|
||||||
child: VideoPlayerWrapper(
|
child: VideoPlayerWrapper(
|
||||||
videoPath: item.videoPath!,
|
videoPath: item.mediaService.storedPath,
|
||||||
mirrorVideo: item.mirrorVideo,
|
|
||||||
),
|
),
|
||||||
// childSize: const Size(300, 300),
|
// childSize: const Size(300, 300),
|
||||||
initialScale: PhotoViewComputedScale.contained,
|
initialScale: PhotoViewComputedScale.contained,
|
||||||
minScale: PhotoViewComputedScale.contained,
|
minScale: PhotoViewComputedScale.contained,
|
||||||
maxScale: PhotoViewComputedScale.covered * 4.1,
|
maxScale: PhotoViewComputedScale.covered * 4.1,
|
||||||
heroAttributes: PhotoViewHeroAttributes(tag: item.id),
|
heroAttributes: PhotoViewHeroAttributes(
|
||||||
|
tag: item.mediaService.mediaFile.mediaId),
|
||||||
)
|
)
|
||||||
: PhotoViewGalleryPageOptions(
|
: PhotoViewGalleryPageOptions(
|
||||||
imageProvider: FileImage(item.imagePath!),
|
imageProvider: FileImage(item.mediaService.storedPath),
|
||||||
initialScale: PhotoViewComputedScale.contained,
|
initialScale: PhotoViewComputedScale.contained,
|
||||||
minScale: PhotoViewComputedScale.contained,
|
minScale: PhotoViewComputedScale.contained,
|
||||||
maxScale: PhotoViewComputedScale.covered * 4.1,
|
maxScale: PhotoViewComputedScale.covered * 4.1,
|
||||||
heroAttributes: PhotoViewHeroAttributes(tag: item.id),
|
heroAttributes: PhotoViewHeroAttributes(
|
||||||
|
tag: item.mediaService.mediaFile.mediaId),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/components/initialsavatar.dart';
|
import 'package:twonly/src/views/components/initialsavatar.dart';
|
||||||
import 'package:twonly/src/views/components/user_context_menu.dart';
|
import 'package:twonly/src/views/components/user_context_menu.component.dart';
|
||||||
|
|
||||||
class PrivacyViewBlockUsers extends StatefulWidget {
|
class PrivacyViewBlockUsers extends StatefulWidget {
|
||||||
const PrivacyViewBlockUsers({super.key});
|
const PrivacyViewBlockUsers({super.key});
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:hashlib/random.dart';
|
import 'package:hashlib/random.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/pow.dart';
|
import 'package:twonly/src/utils/pow.dart';
|
||||||
import 'package:twonly/src/views/components/animate_icon.dart';
|
import 'package:twonly/src/views/components/animate_icon.dart';
|
||||||
|
|
@ -22,15 +20,6 @@ void main() {
|
||||||
expect(await calculatePoW(Uint8List.fromList([41, 41, 41, 41]), 6), 33);
|
expect(await calculatePoW(Uint8List.fromList([41, 41, 41, 41]), 6), 33);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test utils', () async {
|
|
||||||
final list1 = Uint8List.fromList([41, 41, 41, 41, 41, 41, 41]);
|
|
||||||
final list2 = Uint8List.fromList([42, 42, 42]);
|
|
||||||
final combined = combineUint8Lists(list1, list2);
|
|
||||||
final lists = extractUint8Lists(combined);
|
|
||||||
expect(list1, lists[0]);
|
|
||||||
expect(list2, lists[1]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('encode hex', () async {
|
test('encode hex', () async {
|
||||||
final list1 = Uint8List.fromList([41, 41, 41, 41, 41, 41, 41]);
|
final list1 = Uint8List.fromList([41, 41, 41, 41, 41, 41, 41]);
|
||||||
expect(list1, hexToUint8List(uint8ListToHex(list1)));
|
expect(list1, hexToUint8List(uint8ListToHex(list1)));
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue