mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-05-25 05:42:11 +00:00
add shortcuts
This commit is contained in:
parent
ba06126a3c
commit
f2b27e19f2
25 changed files with 5948 additions and 24 deletions
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
## 0.2.11
|
||||
|
||||
- New: Create custom shortcuts to quickly share images with pre-selected groups
|
||||
- New: Seamless recovery for iOS reinstallations
|
||||
- Improved: Redesigned snackbar notifications
|
||||
- Improved: New backup mechanism to allow larger backup files
|
||||
|
|
|
|||
1
assets/animated_icons/distorted_face.json
Normal file
1
assets/animated_icons/distorted_face.json
Normal file
File diff suppressed because one or more lines are too long
83
lib/src/database/daos/shortcuts.dao.dart
Normal file
83
lib/src/database/daos/shortcuts.dao.dart
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import 'package:drift/drift.dart';
|
||||
import 'package:twonly/src/database/tables/shortcuts.table.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
|
||||
part 'shortcuts.dao.g.dart';
|
||||
|
||||
@DriftAccessor(
|
||||
tables: [
|
||||
Shortcuts,
|
||||
ShortcutMembers,
|
||||
],
|
||||
)
|
||||
class ShortcutsDao extends DatabaseAccessor<TwonlyDB> with _$ShortcutsDaoMixin {
|
||||
ShortcutsDao(super.db);
|
||||
|
||||
Stream<List<Shortcut>> watchAllShortcuts() {
|
||||
return (select(shortcuts)..orderBy([
|
||||
(t) =>
|
||||
OrderingTerm(expression: t.usageCounter, mode: OrderingMode.desc),
|
||||
]))
|
||||
.watch();
|
||||
}
|
||||
|
||||
Future<Shortcut?> getShortcutByEmoji(String emoji) {
|
||||
return (select(
|
||||
shortcuts,
|
||||
)..where((t) => t.emoji.equals(emoji))).getSingleOrNull();
|
||||
}
|
||||
|
||||
Future<void> createShortcut(String emoji) async {
|
||||
try {
|
||||
await into(shortcuts).insert(
|
||||
ShortcutsCompanion.insert(emoji: emoji),
|
||||
);
|
||||
// ignore: empty_catches
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
Future<void> addShortcutMembers(int shortcutId, List<String> groupIds) async {
|
||||
await batch((b) {
|
||||
b.insertAll(
|
||||
shortcutMembers,
|
||||
groupIds.map(
|
||||
(gId) => ShortcutMembersCompanion.insert(
|
||||
shortcutId: shortcutId,
|
||||
groupId: gId,
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<List<ShortcutMember>> getShortcutMembers(int shortcutId) {
|
||||
return (select(
|
||||
shortcutMembers,
|
||||
)..where((t) => t.shortcutId.equals(shortcutId))).get();
|
||||
}
|
||||
|
||||
Future<void> incrementUsage(int shortcutId) async {
|
||||
await customStatement(
|
||||
'UPDATE shortcuts SET usage_counter = usage_counter + 1 WHERE id = ?',
|
||||
[shortcutId],
|
||||
);
|
||||
// Notify updates to trigger streams
|
||||
notifyUpdates({TableUpdate.onTable(shortcuts, kind: UpdateKind.update)});
|
||||
}
|
||||
|
||||
Future<void> updateShortcut(int shortcutId, String emoji) async {
|
||||
await (update(shortcuts)..where((t) => t.id.equals(shortcutId))).write(
|
||||
ShortcutsCompanion(emoji: Value(emoji)),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> deleteShortcutMembers(int shortcutId) async {
|
||||
await (delete(
|
||||
shortcutMembers,
|
||||
)..where((t) => t.shortcutId.equals(shortcutId))).go();
|
||||
}
|
||||
|
||||
Future<void> deleteShortcut(int shortcutId) async {
|
||||
await (delete(shortcuts)..where((t) => t.id.equals(shortcutId))).go();
|
||||
}
|
||||
}
|
||||
25
lib/src/database/daos/shortcuts.dao.g.dart
Normal file
25
lib/src/database/daos/shortcuts.dao.g.dart
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'shortcuts.dao.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
mixin _$ShortcutsDaoMixin on DatabaseAccessor<TwonlyDB> {
|
||||
$ShortcutsTable get shortcuts => attachedDatabase.shortcuts;
|
||||
$GroupsTable get groups => attachedDatabase.groups;
|
||||
$ShortcutMembersTable get shortcutMembers => attachedDatabase.shortcutMembers;
|
||||
ShortcutsDaoManager get managers => ShortcutsDaoManager(this);
|
||||
}
|
||||
|
||||
class ShortcutsDaoManager {
|
||||
final _$ShortcutsDaoMixin _db;
|
||||
ShortcutsDaoManager(this._db);
|
||||
$$ShortcutsTableTableManager get shortcuts =>
|
||||
$$ShortcutsTableTableManager(_db.attachedDatabase, _db.shortcuts);
|
||||
$$GroupsTableTableManager get groups =>
|
||||
$$GroupsTableTableManager(_db.attachedDatabase, _db.groups);
|
||||
$$ShortcutMembersTableTableManager get shortcutMembers =>
|
||||
$$ShortcutMembersTableTableManager(
|
||||
_db.attachedDatabase,
|
||||
_db.shortcutMembers,
|
||||
);
|
||||
}
|
||||
2901
lib/src/database/schemas/twonly_db/drift_schema_v13.json
Normal file
2901
lib/src/database/schemas/twonly_db/drift_schema_v13.json
Normal file
File diff suppressed because it is too large
Load diff
26
lib/src/database/tables/shortcuts.table.dart
Normal file
26
lib/src/database/tables/shortcuts.table.dart
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import 'package:drift/drift.dart';
|
||||
import 'package:twonly/src/database/tables/groups.table.dart';
|
||||
|
||||
@DataClassName('Shortcut')
|
||||
class Shortcuts extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
TextColumn get emoji => text().unique()();
|
||||
IntColumn get usageCounter => integer().withDefault(const Constant(0))();
|
||||
}
|
||||
|
||||
@DataClassName('ShortcutMember')
|
||||
class ShortcutMembers extends Table {
|
||||
IntColumn get shortcutId => integer().references(
|
||||
Shortcuts,
|
||||
#id,
|
||||
onDelete: KeyAction.cascade,
|
||||
)();
|
||||
TextColumn get groupId => text().references(
|
||||
Groups,
|
||||
#groupId,
|
||||
onDelete: KeyAction.cascade,
|
||||
)();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {shortcutId, groupId};
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import 'package:twonly/src/database/daos/mediafiles.dao.dart';
|
|||
import 'package:twonly/src/database/daos/messages.dao.dart';
|
||||
import 'package:twonly/src/database/daos/reactions.dao.dart';
|
||||
import 'package:twonly/src/database/daos/receipts.dao.dart';
|
||||
import 'package:twonly/src/database/daos/shortcuts.dao.dart';
|
||||
import 'package:twonly/src/database/daos/user_discovery.dao.dart';
|
||||
import 'package:twonly/src/database/tables/contacts.table.dart';
|
||||
import 'package:twonly/src/database/tables/groups.table.dart';
|
||||
|
|
@ -17,6 +18,7 @@ import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
|||
import 'package:twonly/src/database/tables/messages.table.dart';
|
||||
import 'package:twonly/src/database/tables/reactions.table.dart';
|
||||
import 'package:twonly/src/database/tables/receipts.table.dart';
|
||||
import 'package:twonly/src/database/tables/shortcuts.table.dart';
|
||||
import 'package:twonly/src/database/tables/signal_identity_key_store.table.dart';
|
||||
import 'package:twonly/src/database/tables/signal_pre_key_store.table.dart';
|
||||
import 'package:twonly/src/database/tables/signal_sender_key_store.table.dart';
|
||||
|
|
@ -52,6 +54,8 @@ part 'twonly.db.g.dart';
|
|||
UserDiscoveryOtherPromotions,
|
||||
UserDiscoveryOwnPromotions,
|
||||
UserDiscoveryShares,
|
||||
Shortcuts,
|
||||
ShortcutMembers,
|
||||
],
|
||||
daos: [
|
||||
MessagesDao,
|
||||
|
|
@ -62,6 +66,7 @@ part 'twonly.db.g.dart';
|
|||
MediaFilesDao,
|
||||
UserDiscoveryDao,
|
||||
KeyVerificationDao,
|
||||
ShortcutsDao,
|
||||
],
|
||||
)
|
||||
class TwonlyDB extends _$TwonlyDB {
|
||||
|
|
@ -74,7 +79,7 @@ class TwonlyDB extends _$TwonlyDB {
|
|||
TwonlyDB.forTesting(DatabaseConnection super.connection);
|
||||
|
||||
@override
|
||||
int get schemaVersion => 12;
|
||||
int get schemaVersion => 13;
|
||||
|
||||
static QueryExecutor _openConnection() {
|
||||
return driftDatabase(
|
||||
|
|
@ -186,6 +191,10 @@ class TwonlyDB extends _$TwonlyDB {
|
|||
await m.addColumn(schema.contacts, column);
|
||||
}
|
||||
},
|
||||
from12To13: (m, schema) async {
|
||||
await m.createTable(schema.shortcuts);
|
||||
await m.createTable(schema.shortcutMembers);
|
||||
},
|
||||
)(m, from, to);
|
||||
},
|
||||
);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -6582,6 +6582,480 @@ i1.GeneratedColumn<i2.Uint8List> _column_235(String aliasedName) =>
|
|||
type: i1.DriftSqlType.blob,
|
||||
$customConstraints: 'NOT NULL',
|
||||
);
|
||||
|
||||
final class Schema13 extends i0.VersionedSchema {
|
||||
Schema13({required super.database}) : super(version: 13);
|
||||
@override
|
||||
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||
contacts,
|
||||
groups,
|
||||
mediaFiles,
|
||||
messages,
|
||||
messageHistories,
|
||||
reactions,
|
||||
groupMembers,
|
||||
receipts,
|
||||
receivedReceipts,
|
||||
signalIdentityKeyStores,
|
||||
signalPreKeyStores,
|
||||
signalSenderKeyStores,
|
||||
signalSessionStores,
|
||||
messageActions,
|
||||
groupHistories,
|
||||
keyVerifications,
|
||||
verificationTokens,
|
||||
userDiscoveryAnnouncedUsers,
|
||||
userDiscoveryUserRelations,
|
||||
userDiscoveryOtherPromotions,
|
||||
userDiscoveryOwnPromotions,
|
||||
userDiscoveryShares,
|
||||
shortcuts,
|
||||
shortcutMembers,
|
||||
];
|
||||
late final Shape39 contacts = Shape39(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'contacts',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(user_id)'],
|
||||
columns: [
|
||||
_column_106,
|
||||
_column_107,
|
||||
_column_108,
|
||||
_column_109,
|
||||
_column_110,
|
||||
_column_111,
|
||||
_column_112,
|
||||
_column_113,
|
||||
_column_114,
|
||||
_column_115,
|
||||
_column_116,
|
||||
_column_117,
|
||||
_column_118,
|
||||
_column_211,
|
||||
_column_212,
|
||||
_column_213,
|
||||
_column_214,
|
||||
_column_215,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape23 groups = Shape23(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'groups',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(group_id)'],
|
||||
columns: [
|
||||
_column_119,
|
||||
_column_120,
|
||||
_column_121,
|
||||
_column_122,
|
||||
_column_123,
|
||||
_column_124,
|
||||
_column_125,
|
||||
_column_126,
|
||||
_column_127,
|
||||
_column_128,
|
||||
_column_129,
|
||||
_column_130,
|
||||
_column_131,
|
||||
_column_132,
|
||||
_column_133,
|
||||
_column_134,
|
||||
_column_118,
|
||||
_column_135,
|
||||
_column_136,
|
||||
_column_137,
|
||||
_column_138,
|
||||
_column_139,
|
||||
_column_140,
|
||||
_column_141,
|
||||
_column_142,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape36 mediaFiles = Shape36(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'media_files',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(media_id)'],
|
||||
columns: [
|
||||
_column_143,
|
||||
_column_144,
|
||||
_column_145,
|
||||
_column_146,
|
||||
_column_147,
|
||||
_column_148,
|
||||
_column_149,
|
||||
_column_207,
|
||||
_column_150,
|
||||
_column_151,
|
||||
_column_152,
|
||||
_column_153,
|
||||
_column_154,
|
||||
_column_155,
|
||||
_column_156,
|
||||
_column_157,
|
||||
_column_118,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape25 messages = Shape25(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'messages',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(message_id)'],
|
||||
columns: [
|
||||
_column_158,
|
||||
_column_159,
|
||||
_column_160,
|
||||
_column_144,
|
||||
_column_161,
|
||||
_column_162,
|
||||
_column_163,
|
||||
_column_164,
|
||||
_column_165,
|
||||
_column_153,
|
||||
_column_166,
|
||||
_column_167,
|
||||
_column_168,
|
||||
_column_169,
|
||||
_column_118,
|
||||
_column_170,
|
||||
_column_171,
|
||||
_column_172,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape26 messageHistories = Shape26(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'message_histories',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [
|
||||
_column_173,
|
||||
_column_174,
|
||||
_column_175,
|
||||
_column_161,
|
||||
_column_118,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape27 reactions = Shape27(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'reactions',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(message_id, sender_id, emoji)'],
|
||||
columns: [_column_174, _column_176, _column_177, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape38 groupMembers = Shape38(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'group_members',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(group_id, contact_id)'],
|
||||
columns: [
|
||||
_column_158,
|
||||
_column_178,
|
||||
_column_179,
|
||||
_column_180,
|
||||
_column_209,
|
||||
_column_210,
|
||||
_column_181,
|
||||
_column_118,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape37 receipts = Shape37(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'receipts',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(receipt_id)'],
|
||||
columns: [
|
||||
_column_182,
|
||||
_column_183,
|
||||
_column_184,
|
||||
_column_185,
|
||||
_column_186,
|
||||
_column_208,
|
||||
_column_187,
|
||||
_column_188,
|
||||
_column_189,
|
||||
_column_190,
|
||||
_column_191,
|
||||
_column_118,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape30 receivedReceipts = Shape30(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'received_receipts',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(receipt_id)'],
|
||||
columns: [_column_182, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape31 signalIdentityKeyStores = Shape31(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_identity_key_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(device_id, name)'],
|
||||
columns: [_column_192, _column_193, _column_194, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape32 signalPreKeyStores = Shape32(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_pre_key_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(pre_key_id)'],
|
||||
columns: [_column_195, _column_196, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape11 signalSenderKeyStores = Shape11(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_sender_key_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(sender_key_name)'],
|
||||
columns: [_column_197, _column_198],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape33 signalSessionStores = Shape33(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_session_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(device_id, name)'],
|
||||
columns: [_column_192, _column_193, _column_199, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape34 messageActions = Shape34(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'message_actions',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(message_id, contact_id, type)'],
|
||||
columns: [_column_174, _column_183, _column_144, _column_200],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape35 groupHistories = Shape35(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'group_histories',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(group_history_id)'],
|
||||
columns: [
|
||||
_column_201,
|
||||
_column_158,
|
||||
_column_202,
|
||||
_column_203,
|
||||
_column_204,
|
||||
_column_205,
|
||||
_column_206,
|
||||
_column_144,
|
||||
_column_200,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape40 keyVerifications = Shape40(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'key_verifications',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [_column_216, _column_183, _column_144, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape41 verificationTokens = Shape41(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'verification_tokens',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [_column_217, _column_218, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape42 userDiscoveryAnnouncedUsers = Shape42(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'user_discovery_announced_users',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(announced_user_id)'],
|
||||
columns: [
|
||||
_column_219,
|
||||
_column_220,
|
||||
_column_221,
|
||||
_column_222,
|
||||
_column_223,
|
||||
_column_224,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape43 userDiscoveryUserRelations = Shape43(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'user_discovery_user_relations',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(announced_user_id, from_contact_id)'],
|
||||
columns: [_column_225, _column_226, _column_227],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape44 userDiscoveryOtherPromotions = Shape44(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'user_discovery_other_promotions',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(from_contact_id, public_id)'],
|
||||
columns: [
|
||||
_column_226,
|
||||
_column_228,
|
||||
_column_229,
|
||||
_column_230,
|
||||
_column_231,
|
||||
_column_227,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape45 userDiscoveryOwnPromotions = Shape45(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'user_discovery_own_promotions',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [_column_232, _column_183, _column_233],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape46 userDiscoveryShares = Shape46(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'user_discovery_shares',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [_column_234, _column_235, _column_175],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape47 shortcuts = Shape47(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'shortcuts',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [_column_173, _column_236, _column_237],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape48 shortcutMembers = Shape48(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'shortcut_members',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(shortcut_id, group_id)'],
|
||||
columns: [_column_238, _column_158],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
}
|
||||
|
||||
class Shape47 extends i0.VersionedTable {
|
||||
Shape47({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<int> get id =>
|
||||
columnsByName['id']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<String> get emoji =>
|
||||
columnsByName['emoji']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<int> get usageCounter =>
|
||||
columnsByName['usage_counter']! as i1.GeneratedColumn<int>;
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<String> _column_236(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>(
|
||||
'emoji',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i1.DriftSqlType.string,
|
||||
$customConstraints: 'NOT NULL UNIQUE',
|
||||
);
|
||||
i1.GeneratedColumn<int> _column_237(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>(
|
||||
'usage_counter',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i1.DriftSqlType.int,
|
||||
$customConstraints: 'NOT NULL DEFAULT 0',
|
||||
defaultValue: const i1.CustomExpression('0'),
|
||||
);
|
||||
|
||||
class Shape48 extends i0.VersionedTable {
|
||||
Shape48({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<int> get shortcutId =>
|
||||
columnsByName['shortcut_id']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<String> get groupId =>
|
||||
columnsByName['group_id']! as i1.GeneratedColumn<String>;
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<int> _column_238(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>(
|
||||
'shortcut_id',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i1.DriftSqlType.int,
|
||||
$customConstraints: 'NOT NULL REFERENCES shortcuts(id)ON DELETE CASCADE',
|
||||
);
|
||||
i0.MigrationStepWithVersion migrationSteps({
|
||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
||||
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
||||
|
|
@ -6594,6 +7068,7 @@ i0.MigrationStepWithVersion migrationSteps({
|
|||
required Future<void> Function(i1.Migrator m, Schema10 schema) from9To10,
|
||||
required Future<void> Function(i1.Migrator m, Schema11 schema) from10To11,
|
||||
required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12,
|
||||
required Future<void> Function(i1.Migrator m, Schema13 schema) from12To13,
|
||||
}) {
|
||||
return (currentVersion, database) async {
|
||||
switch (currentVersion) {
|
||||
|
|
@ -6652,6 +7127,11 @@ i0.MigrationStepWithVersion migrationSteps({
|
|||
final migrator = i1.Migrator(database, schema);
|
||||
await from11To12(migrator, schema);
|
||||
return 12;
|
||||
case 12:
|
||||
final schema = Schema13(database: database);
|
||||
final migrator = i1.Migrator(database, schema);
|
||||
await from12To13(migrator, schema);
|
||||
return 13;
|
||||
default:
|
||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||
}
|
||||
|
|
@ -6670,6 +7150,7 @@ i1.OnUpgrade stepByStep({
|
|||
required Future<void> Function(i1.Migrator m, Schema10 schema) from9To10,
|
||||
required Future<void> Function(i1.Migrator m, Schema11 schema) from10To11,
|
||||
required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12,
|
||||
required Future<void> Function(i1.Migrator m, Schema13 schema) from12To13,
|
||||
}) => i0.VersionedSchema.stepByStepHelper(
|
||||
step: migrationSteps(
|
||||
from1To2: from1To2,
|
||||
|
|
@ -6683,5 +7164,6 @@ i1.OnUpgrade stepByStep({
|
|||
from9To10: from9To10,
|
||||
from10To11: from10To11,
|
||||
from11To12: from11To12,
|
||||
from12To13: from12To13,
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3115,6 +3115,48 @@ abstract class AppLocalizations {
|
|||
/// In en, this message translates to:
|
||||
/// **'Registering new account'**
|
||||
String get registeringNewAccount;
|
||||
|
||||
/// No description provided for @createShortcut.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Create shortcut'**
|
||||
String get createShortcut;
|
||||
|
||||
/// No description provided for @editShortcut.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit shortcut'**
|
||||
String get editShortcut;
|
||||
|
||||
/// No description provided for @deleteShortcut.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Delete shortcut'**
|
||||
String get deleteShortcut;
|
||||
|
||||
/// No description provided for @deleteShortcutBody.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Are you sure you want to delete this shortcut?'**
|
||||
String get deleteShortcutBody;
|
||||
|
||||
/// No description provided for @updateShortcut.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Update shortcut'**
|
||||
String get updateShortcut;
|
||||
|
||||
/// No description provided for @selectEmoji.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Select Emoji'**
|
||||
String get selectEmoji;
|
||||
|
||||
/// No description provided for @errorEmojiUsedOrInvalid.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Emoji already used or invalid'**
|
||||
String get errorEmojiUsedOrInvalid;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
|
|
|||
|
|
@ -1757,4 +1757,27 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get registeringNewAccount => 'Neues Konto wird registriert';
|
||||
|
||||
@override
|
||||
String get createShortcut => 'Shortcut erstellen';
|
||||
|
||||
@override
|
||||
String get editShortcut => 'Shortcut bearbeiten';
|
||||
|
||||
@override
|
||||
String get deleteShortcut => 'Shortcut löschen';
|
||||
|
||||
@override
|
||||
String get deleteShortcutBody =>
|
||||
'Bist du sicher, dass du diesen Shortcut löschen möchtest?';
|
||||
|
||||
@override
|
||||
String get updateShortcut => 'Shortcut aktualisieren';
|
||||
|
||||
@override
|
||||
String get selectEmoji => 'Emoji auswählen';
|
||||
|
||||
@override
|
||||
String get errorEmojiUsedOrInvalid =>
|
||||
'Emoji wird bereits verwendet oder ist ungültig';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1742,4 +1742,26 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get registeringNewAccount => 'Registering new account';
|
||||
|
||||
@override
|
||||
String get createShortcut => 'Create shortcut';
|
||||
|
||||
@override
|
||||
String get editShortcut => 'Edit shortcut';
|
||||
|
||||
@override
|
||||
String get deleteShortcut => 'Delete shortcut';
|
||||
|
||||
@override
|
||||
String get deleteShortcutBody =>
|
||||
'Are you sure you want to delete this shortcut?';
|
||||
|
||||
@override
|
||||
String get updateShortcut => 'Update shortcut';
|
||||
|
||||
@override
|
||||
String get selectEmoji => 'Select Emoji';
|
||||
|
||||
@override
|
||||
String get errorEmojiUsedOrInvalid => 'Emoji already used or invalid';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 75b97e912f2e72a8e2a5da65e8ad12f0d1091855
|
||||
Subproject commit 9218abf0961c072edd2f8aa5035d06a331b853c6
|
||||
|
|
@ -417,6 +417,7 @@ class ApiService {
|
|||
),
|
||||
);
|
||||
}
|
||||
await twonlyDB.receiptsDao.deleteReceiptForUser(contactId);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ Future<void> createThumbnailsForVideo(
|
|||
'It took ${stopwatch.elapsedMilliseconds}ms to create the thumbnail.',
|
||||
);
|
||||
} else {
|
||||
Log.error(
|
||||
Log.warn(
|
||||
'Thumbnail creation failed for the video with exit code.',
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,10 @@ class Log {
|
|||
static String filterLogMessage(String msg) {
|
||||
if (msg.contains('SqliteException')) {
|
||||
// Do not log data which would be inserted into the DB.
|
||||
return msg.substring(0, msg.indexOf('parameters: '));
|
||||
final paramIndex = msg.indexOf('parameters: ');
|
||||
if (paramIndex != -1) {
|
||||
return msg.substring(0, paramIndex);
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ class EmojiAnimationComp extends StatelessWidget {
|
|||
'😴': 'sleep.lottie',
|
||||
'🤒': 'thermometer-face.lottie',
|
||||
'🤕': 'bandage-face.lottie',
|
||||
'': 'distorted_face.json',
|
||||
'🤥': 'liar.lottie',
|
||||
'😇': 'halo.lottie',
|
||||
'🤠': 'cowboy.lottie',
|
||||
|
|
|
|||
292
lib/src/visual/views/camera/add_new_shortcut.view.dart
Normal file
292
lib/src/visual/views/camera/add_new_shortcut.view.dart
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/locator.dart';
|
||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
||||
import 'package:twonly/src/visual/components/emoji_picker.bottom.dart';
|
||||
import 'package:twonly/src/visual/components/flame_counter.comp.dart';
|
||||
import 'package:twonly/src/visual/components/snackbar.dart';
|
||||
import 'package:twonly/src/visual/decorations/input_text.decoration.dart';
|
||||
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layer_data.dart';
|
||||
|
||||
class AddNewShortcutView extends StatefulWidget {
|
||||
const AddNewShortcutView({this.shortcut, super.key});
|
||||
final Shortcut? shortcut;
|
||||
@override
|
||||
State<AddNewShortcutView> createState() => _StartNewChatView();
|
||||
}
|
||||
|
||||
class _StartNewChatView extends State<AddNewShortcutView> {
|
||||
List<Group> _groups = [];
|
||||
List<Group> _allGroups = [];
|
||||
final TextEditingController _searchGroupName = TextEditingController();
|
||||
late StreamSubscription<List<Group>> _groupSub;
|
||||
|
||||
final HashSet<String> _selectedGroups = HashSet();
|
||||
String? shortcutEmoji;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.shortcut != null) {
|
||||
shortcutEmoji = widget.shortcut!.emoji;
|
||||
twonlyDB.shortcutsDao.getShortcutMembers(widget.shortcut!.id).then((
|
||||
members,
|
||||
) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
for (final m in members) {
|
||||
_selectedGroups.add(m.groupId);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
final stream = twonlyDB.groupsDao.watchGroupsForChatList();
|
||||
|
||||
_groupSub = stream.listen((update) async {
|
||||
update.sort(
|
||||
(a, b) => a.groupName.compareTo(b.groupName),
|
||||
);
|
||||
setState(() {
|
||||
_allGroups = update;
|
||||
});
|
||||
await filterUsers();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
unawaited(_groupSub.cancel());
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> filterUsers() async {
|
||||
if (_searchGroupName.value.text.isEmpty) {
|
||||
setState(() {
|
||||
_groups = _allGroups;
|
||||
});
|
||||
return;
|
||||
}
|
||||
final usersFiltered = _allGroups
|
||||
.where(
|
||||
(group) => group.groupName.toLowerCase().contains(
|
||||
_searchGroupName.value.text.toLowerCase(),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
setState(() {
|
||||
_groups = usersFiltered;
|
||||
});
|
||||
}
|
||||
|
||||
void toggleSelectedGroup(String groupId) {
|
||||
if (!_selectedGroups.contains(groupId)) {
|
||||
if (_selectedGroups.length > 256) {
|
||||
showSnackbar(context, context.lang.groupSizeLimitError(256));
|
||||
return;
|
||||
}
|
||||
_selectedGroups.add(groupId);
|
||||
} else {
|
||||
_selectedGroups.remove(groupId);
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Future<void> submitChanges() async {
|
||||
try {
|
||||
if (widget.shortcut != null) {
|
||||
await twonlyDB.shortcutsDao.updateShortcut(
|
||||
widget.shortcut!.id,
|
||||
shortcutEmoji!,
|
||||
);
|
||||
await twonlyDB.shortcutsDao.deleteShortcutMembers(widget.shortcut!.id);
|
||||
await twonlyDB.shortcutsDao.addShortcutMembers(
|
||||
widget.shortcut!.id,
|
||||
_selectedGroups.toList(),
|
||||
);
|
||||
} else {
|
||||
await twonlyDB.shortcutsDao.createShortcut(
|
||||
shortcutEmoji!,
|
||||
);
|
||||
final shortcutId = (await twonlyDB.shortcutsDao.getShortcutByEmoji(
|
||||
shortcutEmoji!,
|
||||
))!.id;
|
||||
await twonlyDB.shortcutsDao.deleteShortcutMembers(shortcutId);
|
||||
await twonlyDB.shortcutsDao.addShortcutMembers(
|
||||
shortcutId,
|
||||
_selectedGroups.toList(),
|
||||
);
|
||||
}
|
||||
if (mounted) Navigator.pop(context);
|
||||
} catch (e) {
|
||||
Log.error(e);
|
||||
if (mounted) {
|
||||
showSnackbar(context, context.lang.errorEmojiUsedOrInvalid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () => FocusScope.of(context).unfocus(),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
widget.shortcut == null
|
||||
? context.lang.createShortcut
|
||||
: context.lang.editShortcut,
|
||||
),
|
||||
actions: [
|
||||
if (widget.shortcut != null)
|
||||
IconButton(
|
||||
icon: const FaIcon(
|
||||
FontAwesomeIcons.trashCan,
|
||||
size: 18,
|
||||
color: Colors.red,
|
||||
),
|
||||
onPressed: () async {
|
||||
final confirm = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(context.lang.deleteShortcut),
|
||||
content: Text(context.lang.deleteShortcutBody),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: Text(context.lang.cancel),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: Text(context.lang.delete),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (confirm == true) {
|
||||
await twonlyDB.shortcutsDao.deleteShortcut(
|
||||
widget.shortcut!.id,
|
||||
);
|
||||
if (context.mounted) Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
// ignore: inference_failure_on_function_invocation
|
||||
final result = await showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Colors.black,
|
||||
builder: (context) => const EmojiPickerBottom(),
|
||||
);
|
||||
if (result is EmojiLayerData) {
|
||||
setState(() {
|
||||
shortcutEmoji = result.text;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
shortcutEmoji ?? context.lang.selectEmoji,
|
||||
style: TextStyle(
|
||||
fontSize: shortcutEmoji == null ? 14 : 22,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FilledButton.icon(
|
||||
onPressed: (_selectedGroups.isEmpty || shortcutEmoji == null)
|
||||
? null
|
||||
: submitChanges,
|
||||
label: Text(
|
||||
widget.shortcut == null
|
||||
? context.lang.createShortcut
|
||||
: context.lang.updateShortcut,
|
||||
),
|
||||
icon: const FaIcon(FontAwesomeIcons.check),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 40,
|
||||
left: 10,
|
||||
top: 20,
|
||||
right: 10,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: TextField(
|
||||
onChanged: (_) async {
|
||||
await filterUsers();
|
||||
},
|
||||
controller: _searchGroupName,
|
||||
decoration: getInputDecoration(
|
||||
context,
|
||||
context.lang.shareImageSearchAllContacts,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
restorationId: 'new_message_users_list',
|
||||
itemCount: _groups.length,
|
||||
itemBuilder: (context, i) {
|
||||
final group = _groups[i];
|
||||
return ListTile(
|
||||
key: ValueKey(group.groupId),
|
||||
title: Row(
|
||||
children: [
|
||||
Text(substringBy(group.groupName, 12)),
|
||||
FlameCounterWidget(
|
||||
groupId: group.groupId,
|
||||
prefix: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
leading: AvatarIcon(
|
||||
group: group,
|
||||
fontSize: 15,
|
||||
),
|
||||
trailing: Checkbox(
|
||||
value: _selectedGroups.contains(group.groupId),
|
||||
side: WidgetStateBorderSide.resolveWith(
|
||||
(states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return const BorderSide(width: 0);
|
||||
}
|
||||
return BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
);
|
||||
},
|
||||
),
|
||||
onChanged: (value) {
|
||||
toggleSelectedGroup(group.groupId);
|
||||
},
|
||||
),
|
||||
onTap: () {
|
||||
toggleSelectedGroup(group.groupId);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -98,9 +98,12 @@ class MainCameraController {
|
|||
final cameraControllerTemp = cameraController;
|
||||
cameraController = null;
|
||||
// prevents: CameraException(Disposed CameraController, buildPreview() was called on a disposed CameraController.)
|
||||
_pendingDisposal = Future.delayed(const Duration(milliseconds: 100), () async {
|
||||
_pendingDisposal = Future.delayed(
|
||||
const Duration(milliseconds: 100),
|
||||
() async {
|
||||
await cameraControllerTemp?.dispose();
|
||||
});
|
||||
},
|
||||
);
|
||||
initCameraStarted = false;
|
||||
selectedCameraDetails = SelectedCameraDetails();
|
||||
}
|
||||
|
|
@ -226,7 +229,7 @@ class MainCameraController {
|
|||
(e.code == 'setFocusPointFailed' || e.code == 'setFocusModeFailed')) {
|
||||
Log.info('Focus point or mode not supported on this device');
|
||||
} else {
|
||||
Log.error(e);
|
||||
Log.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import 'package:twonly/src/visual/decorations/input_text.decoration.dart';
|
|||
import 'package:twonly/src/visual/elements/headline.element.dart';
|
||||
import 'package:twonly/src/visual/helpers/screenshot.helper.dart';
|
||||
import 'package:twonly/src/visual/views/camera/share_image_contact_selection_components/best_friends_selector.dart';
|
||||
import 'package:twonly/src/visual/views/camera/share_image_contact_selection_components/shortcut_row.comp.dart';
|
||||
import 'package:twonly/src/visual/views/camera/share_image_editor_components/layers/background.layer.dart';
|
||||
|
||||
class ShareImageView extends StatefulWidget {
|
||||
|
|
@ -194,6 +195,11 @@ class _ShareImageView extends State<ShareImageView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
ShortcutRowComp(
|
||||
selectedGroupIds: widget.selectedGroupIds,
|
||||
updateSelectedGroupIds: updateSelectedGroupIds,
|
||||
),
|
||||
if (_pinnedContacts.isNotEmpty) const SizedBox(height: 10),
|
||||
BestFriendsSelector(
|
||||
groups: _pinnedContacts,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
import 'dart:collection';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/locator.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/visual/views/camera/add_new_shortcut.view.dart';
|
||||
|
||||
class ShortcutRowComp extends StatefulWidget {
|
||||
const ShortcutRowComp({
|
||||
required this.selectedGroupIds,
|
||||
required this.updateSelectedGroupIds,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final HashSet<String> selectedGroupIds;
|
||||
final void Function(String, bool) updateSelectedGroupIds;
|
||||
|
||||
@override
|
||||
State<ShortcutRowComp> createState() => _ShortcutRowCompState();
|
||||
}
|
||||
|
||||
class _ShortcutRowCompState extends State<ShortcutRowComp> {
|
||||
Future<void> _openCreateDialog() async {
|
||||
await context.navPush(const AddNewShortcutView());
|
||||
}
|
||||
|
||||
Future<void> _applyShortcut(Shortcut shortcut) async {
|
||||
await twonlyDB.shortcutsDao.incrementUsage(shortcut.id);
|
||||
final members = await twonlyDB.shortcutsDao.getShortcutMembers(shortcut.id);
|
||||
for (final m in members) {
|
||||
widget.updateSelectedGroupIds(m.groupId, true);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 40,
|
||||
child: StreamBuilder<List<Shortcut>>(
|
||||
stream: twonlyDB.shortcutsDao.watchAllShortcuts(),
|
||||
builder: (context, snapshot) {
|
||||
final shortcuts = snapshot.data ?? [];
|
||||
return ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
ActionChip(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: _openCreateDialog,
|
||||
label: shortcuts.isEmpty
|
||||
? Text(
|
||||
context.lang.createShortcut,
|
||||
style: const TextStyle(fontSize: 9),
|
||||
)
|
||||
: const Icon(Icons.add_reaction_outlined, size: 20),
|
||||
shape: const StadiumBorder(),
|
||||
),
|
||||
for (final shortcut in shortcuts)
|
||||
GestureDetector(
|
||||
onLongPress: () {
|
||||
context.navPush(AddNewShortcutView(shortcut: shortcut));
|
||||
},
|
||||
child: ActionChip(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () => _applyShortcut(shortcut),
|
||||
label: Text(
|
||||
shortcut.emoji,
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
shape: const StadiumBorder(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -443,8 +443,8 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: c5bffd3414c1e640389b41165b831df7df1cf517
|
||||
ref: "23a0595f7dde50728afce917c1c58f284ccbb495"
|
||||
resolved-ref: "23a0595f7dde50728afce917c1c58f284ccbb495"
|
||||
url: "https://github.com/otsmr/emoji_picker_flutter.git"
|
||||
source: git
|
||||
version: "4.4.0"
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ dependency_overrides:
|
|||
# Using override until this gets merged.
|
||||
git:
|
||||
url: https://github.com/otsmr/emoji_picker_flutter.git
|
||||
ref: 23a0595f7dde50728afce917c1c58f284ccbb495
|
||||
flutter_android_volume_keydown:
|
||||
git:
|
||||
url: https://github.com/yenchieh/flutter_android_volume_keydown.git
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import 'schema_v9.dart' as v9;
|
|||
import 'schema_v10.dart' as v10;
|
||||
import 'schema_v11.dart' as v11;
|
||||
import 'schema_v12.dart' as v12;
|
||||
import 'schema_v13.dart' as v13;
|
||||
|
||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||
@override
|
||||
|
|
@ -45,10 +46,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
|
|||
return v11.DatabaseAtV11(db);
|
||||
case 12:
|
||||
return v12.DatabaseAtV12(db);
|
||||
case 13:
|
||||
return v13.DatabaseAtV13(db);
|
||||
default:
|
||||
throw MissingSchemaException(version, versions);
|
||||
}
|
||||
}
|
||||
|
||||
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
|
||||
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue