✨ Reworked post draft
This commit is contained in:
		| @@ -471,6 +471,8 @@ | |||||||
|   "close": "Close", |   "close": "Close", | ||||||
|   "drafts": "Drafts", |   "drafts": "Drafts", | ||||||
|   "noDrafts": "No drafts yet", |   "noDrafts": "No drafts yet", | ||||||
|  |   "searchDrafts": "Search drafts...", | ||||||
|  |   "noSearchResults": "No search results", | ||||||
|   "articleDrafts": "Article drafts", |   "articleDrafts": "Article drafts", | ||||||
|   "postDrafts": "Post drafts", |   "postDrafts": "Post drafts", | ||||||
|   "saveDraft": "Save draft", |   "saveDraft": "Save draft", | ||||||
|   | |||||||
| @@ -2,8 +2,15 @@ import 'package:drift/drift.dart'; | |||||||
|  |  | ||||||
| class PostDrafts extends Table { | class PostDrafts extends Table { | ||||||
|   TextColumn get id => text()(); |   TextColumn get id => text()(); | ||||||
|   TextColumn get post => text()(); // Store SnPost model as JSON string |   // Searchable fields stored separately for performance | ||||||
|  |   TextColumn get title => text().nullable()(); | ||||||
|  |   TextColumn get description => text().nullable()(); | ||||||
|  |   TextColumn get content => text().nullable()(); | ||||||
|  |   IntColumn get visibility => integer().withDefault(const Constant(0))(); | ||||||
|  |   IntColumn get type => integer().withDefault(const Constant(0))(); | ||||||
|   DateTimeColumn get lastModified => dateTime()(); |   DateTimeColumn get lastModified => dateTime()(); | ||||||
|  |   // Full post data stored as JSON for complete restoration | ||||||
|  |   TextColumn get postData => text()(); | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Set<Column> get primaryKey => {id}; |   Set<Column> get primaryKey => {id}; | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ class AppDatabase extends _$AppDatabase { | |||||||
|   AppDatabase(super.e); |   AppDatabase(super.e); | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   int get schemaVersion => 4; |   int get schemaVersion => 6; | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   MigrationStrategy get migration => MigrationStrategy( |   MigrationStrategy get migration => MigrationStrategy( | ||||||
| @@ -28,9 +28,67 @@ class AppDatabase extends _$AppDatabase { | |||||||
|         // Drop old draft tables if they exist |         // Drop old draft tables if they exist | ||||||
|         await m.createTable(postDrafts); |         await m.createTable(postDrafts); | ||||||
|       } |       } | ||||||
|  |       if (from < 6) { | ||||||
|  |         // Migrate from old schema to new schema with separate searchable fields | ||||||
|  |         await _migrateToVersion6(m); | ||||||
|  |       } | ||||||
|     }, |     }, | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  |   Future<void> _migrateToVersion6(Migrator m) async { | ||||||
|  |     // Rename existing table to old if it exists | ||||||
|  |     try { | ||||||
|  |       await customStatement( | ||||||
|  |         'ALTER TABLE post_drafts RENAME TO post_drafts_old', | ||||||
|  |       ); | ||||||
|  |     } catch (e) { | ||||||
|  |       // Table might not exist | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Drop the table | ||||||
|  |     await customStatement('DROP TABLE IF EXISTS post_drafts'); | ||||||
|  |  | ||||||
|  |     // Create new table | ||||||
|  |     await m.createTable(postDrafts); | ||||||
|  |  | ||||||
|  |     // Migrate existing data if any | ||||||
|  |     try { | ||||||
|  |       final oldDrafts = | ||||||
|  |           await customSelect( | ||||||
|  |             'SELECT id, post, lastModified FROM post_drafts_old', | ||||||
|  |             readsFrom: {postDrafts}, | ||||||
|  |           ).get(); | ||||||
|  |  | ||||||
|  |       for (final row in oldDrafts) { | ||||||
|  |         final postJson = row.read<String>('post'); | ||||||
|  |         final id = row.read<String>('id'); | ||||||
|  |         final lastModified = row.read<DateTime>('lastModified'); | ||||||
|  |  | ||||||
|  |         if (postJson.isNotEmpty) { | ||||||
|  |           final post = SnPost.fromJson(jsonDecode(postJson)); | ||||||
|  |           await into(postDrafts).insert( | ||||||
|  |             PostDraftsCompanion( | ||||||
|  |               id: Value(id), | ||||||
|  |               title: Value(post.title), | ||||||
|  |               description: Value(post.description), | ||||||
|  |               content: Value(post.content), | ||||||
|  |               visibility: Value(post.visibility), | ||||||
|  |               type: Value(post.type), | ||||||
|  |               lastModified: Value(lastModified), | ||||||
|  |               postData: Value(postJson), | ||||||
|  |             ), | ||||||
|  |           ); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // Drop old table | ||||||
|  |       await customStatement('DROP TABLE IF EXISTS post_drafts_old'); | ||||||
|  |     } catch (e) { | ||||||
|  |       // If migration fails, just recreate the table | ||||||
|  |       await m.createTable(postDrafts); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   // Methods for chat messages |   // Methods for chat messages | ||||||
|   Future<List<ChatMessage>> getMessagesForRoom( |   Future<List<ChatMessage>> getMessagesForRoom( | ||||||
|     String roomId, { |     String roomId, { | ||||||
| @@ -69,7 +127,9 @@ class AppDatabase extends _$AppDatabase { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<int> getTotalMessagesForRoom(String roomId) { |   Future<int> getTotalMessagesForRoom(String roomId) { | ||||||
|     return (select(chatMessages)..where((m) => m.roomId.equals(roomId))).get().then((list) => list.length); |     return (select( | ||||||
|  |       chatMessages, | ||||||
|  |     )..where((m) => m.roomId.equals(roomId))).get().then((list) => list.length); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<List<LocalChatMessage>> searchMessages( |   Future<List<LocalChatMessage>> searchMessages( | ||||||
| @@ -85,10 +145,6 @@ class AppDatabase extends _$AppDatabase { | |||||||
|             ..where((m) => m.content.like('%${query.toLowerCase()}%')); |             ..where((m) => m.content.like('%${query.toLowerCase()}%')); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|      |  | ||||||
|  |  | ||||||
|      |  | ||||||
|  |  | ||||||
|     final messages = |     final messages = | ||||||
|         await (selectStatement |         await (selectStatement | ||||||
|               ..orderBy([(m) => OrderingTerm.desc(m.createdAt)])) |               ..orderBy([(m) => OrderingTerm.desc(m.createdAt)])) | ||||||
| @@ -129,10 +185,31 @@ class AppDatabase extends _$AppDatabase { | |||||||
|   Future<List<SnPost>> getAllPostDrafts() async { |   Future<List<SnPost>> getAllPostDrafts() async { | ||||||
|     final drafts = await select(postDrafts).get(); |     final drafts = await select(postDrafts).get(); | ||||||
|     return drafts |     return drafts | ||||||
|         .map((draft) => SnPost.fromJson(jsonDecode(draft.post))) |         .map((draft) => SnPost.fromJson(jsonDecode(draft.postData))) | ||||||
|         .toList(); |         .toList(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   Future<List<PostDraft>> getAllPostDraftRecords() async { | ||||||
|  |     return await select(postDrafts).get(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Future<List<PostDraft>> searchPostDrafts(String query) async { | ||||||
|  |     if (query.isEmpty) { | ||||||
|  |       return await select(postDrafts).get(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     final searchTerm = '%${query.toLowerCase()}%'; | ||||||
|  |     return await (select(postDrafts) | ||||||
|  |           ..where( | ||||||
|  |             (draft) => | ||||||
|  |                 draft.title.like(searchTerm) | | ||||||
|  |                 draft.description.like(searchTerm) | | ||||||
|  |                 draft.content.like(searchTerm), | ||||||
|  |           ) | ||||||
|  |           ..orderBy([(draft) => OrderingTerm.desc(draft.lastModified)])) | ||||||
|  |         .get(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   Future<void> addPostDraft(PostDraftsCompanion entry) async { |   Future<void> addPostDraft(PostDraftsCompanion entry) async { | ||||||
|     await into(postDrafts).insert(entry, mode: InsertMode.replace); |     await into(postDrafts).insert(entry, mode: InsertMode.replace); | ||||||
|   } |   } | ||||||
| @@ -144,4 +221,9 @@ class AppDatabase extends _$AppDatabase { | |||||||
|   Future<void> clearAllPostDrafts() async { |   Future<void> clearAllPostDrafts() async { | ||||||
|     await delete(postDrafts).go(); |     await delete(postDrafts).go(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   Future<PostDraft?> getPostDraftById(String id) async { | ||||||
|  |     return await (select(postDrafts) | ||||||
|  |       ..where((tbl) => tbl.id.equals(id))).getSingleOrNull(); | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -584,14 +584,58 @@ class $PostDraftsTable extends PostDrafts | |||||||
|     type: DriftSqlType.string, |     type: DriftSqlType.string, | ||||||
|     requiredDuringInsert: true, |     requiredDuringInsert: true, | ||||||
|   ); |   ); | ||||||
|   static const VerificationMeta _postMeta = const VerificationMeta('post'); |   static const VerificationMeta _titleMeta = const VerificationMeta('title'); | ||||||
|   @override |   @override | ||||||
|   late final GeneratedColumn<String> post = GeneratedColumn<String>( |   late final GeneratedColumn<String> title = GeneratedColumn<String>( | ||||||
|     'post', |     'title', | ||||||
|  |     aliasedName, | ||||||
|  |     true, | ||||||
|  |     type: DriftSqlType.string, | ||||||
|  |     requiredDuringInsert: false, | ||||||
|  |   ); | ||||||
|  |   static const VerificationMeta _descriptionMeta = const VerificationMeta( | ||||||
|  |     'description', | ||||||
|  |   ); | ||||||
|  |   @override | ||||||
|  |   late final GeneratedColumn<String> description = GeneratedColumn<String>( | ||||||
|  |     'description', | ||||||
|  |     aliasedName, | ||||||
|  |     true, | ||||||
|  |     type: DriftSqlType.string, | ||||||
|  |     requiredDuringInsert: false, | ||||||
|  |   ); | ||||||
|  |   static const VerificationMeta _contentMeta = const VerificationMeta( | ||||||
|  |     'content', | ||||||
|  |   ); | ||||||
|  |   @override | ||||||
|  |   late final GeneratedColumn<String> content = GeneratedColumn<String>( | ||||||
|  |     'content', | ||||||
|  |     aliasedName, | ||||||
|  |     true, | ||||||
|  |     type: DriftSqlType.string, | ||||||
|  |     requiredDuringInsert: false, | ||||||
|  |   ); | ||||||
|  |   static const VerificationMeta _visibilityMeta = const VerificationMeta( | ||||||
|  |     'visibility', | ||||||
|  |   ); | ||||||
|  |   @override | ||||||
|  |   late final GeneratedColumn<int> visibility = GeneratedColumn<int>( | ||||||
|  |     'visibility', | ||||||
|     aliasedName, |     aliasedName, | ||||||
|     false, |     false, | ||||||
|     type: DriftSqlType.string, |     type: DriftSqlType.int, | ||||||
|     requiredDuringInsert: true, |     requiredDuringInsert: false, | ||||||
|  |     defaultValue: const Constant(0), | ||||||
|  |   ); | ||||||
|  |   static const VerificationMeta _typeMeta = const VerificationMeta('type'); | ||||||
|  |   @override | ||||||
|  |   late final GeneratedColumn<int> type = GeneratedColumn<int>( | ||||||
|  |     'type', | ||||||
|  |     aliasedName, | ||||||
|  |     false, | ||||||
|  |     type: DriftSqlType.int, | ||||||
|  |     requiredDuringInsert: false, | ||||||
|  |     defaultValue: const Constant(0), | ||||||
|   ); |   ); | ||||||
|   static const VerificationMeta _lastModifiedMeta = const VerificationMeta( |   static const VerificationMeta _lastModifiedMeta = const VerificationMeta( | ||||||
|     'lastModified', |     'lastModified', | ||||||
| @@ -604,8 +648,28 @@ class $PostDraftsTable extends PostDrafts | |||||||
|     type: DriftSqlType.dateTime, |     type: DriftSqlType.dateTime, | ||||||
|     requiredDuringInsert: true, |     requiredDuringInsert: true, | ||||||
|   ); |   ); | ||||||
|  |   static const VerificationMeta _postDataMeta = const VerificationMeta( | ||||||
|  |     'postData', | ||||||
|  |   ); | ||||||
|   @override |   @override | ||||||
|   List<GeneratedColumn> get $columns => [id, post, lastModified]; |   late final GeneratedColumn<String> postData = GeneratedColumn<String>( | ||||||
|  |     'post_data', | ||||||
|  |     aliasedName, | ||||||
|  |     false, | ||||||
|  |     type: DriftSqlType.string, | ||||||
|  |     requiredDuringInsert: true, | ||||||
|  |   ); | ||||||
|  |   @override | ||||||
|  |   List<GeneratedColumn> get $columns => [ | ||||||
|  |     id, | ||||||
|  |     title, | ||||||
|  |     description, | ||||||
|  |     content, | ||||||
|  |     visibility, | ||||||
|  |     type, | ||||||
|  |     lastModified, | ||||||
|  |     postData, | ||||||
|  |   ]; | ||||||
|   @override |   @override | ||||||
|   String get aliasedName => _alias ?? actualTableName; |   String get aliasedName => _alias ?? actualTableName; | ||||||
|   @override |   @override | ||||||
| @@ -623,13 +687,38 @@ class $PostDraftsTable extends PostDrafts | |||||||
|     } else if (isInserting) { |     } else if (isInserting) { | ||||||
|       context.missing(_idMeta); |       context.missing(_idMeta); | ||||||
|     } |     } | ||||||
|     if (data.containsKey('post')) { |     if (data.containsKey('title')) { | ||||||
|       context.handle( |       context.handle( | ||||||
|         _postMeta, |         _titleMeta, | ||||||
|         post.isAcceptableOrUnknown(data['post']!, _postMeta), |         title.isAcceptableOrUnknown(data['title']!, _titleMeta), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     if (data.containsKey('description')) { | ||||||
|  |       context.handle( | ||||||
|  |         _descriptionMeta, | ||||||
|  |         description.isAcceptableOrUnknown( | ||||||
|  |           data['description']!, | ||||||
|  |           _descriptionMeta, | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     if (data.containsKey('content')) { | ||||||
|  |       context.handle( | ||||||
|  |         _contentMeta, | ||||||
|  |         content.isAcceptableOrUnknown(data['content']!, _contentMeta), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     if (data.containsKey('visibility')) { | ||||||
|  |       context.handle( | ||||||
|  |         _visibilityMeta, | ||||||
|  |         visibility.isAcceptableOrUnknown(data['visibility']!, _visibilityMeta), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     if (data.containsKey('type')) { | ||||||
|  |       context.handle( | ||||||
|  |         _typeMeta, | ||||||
|  |         type.isAcceptableOrUnknown(data['type']!, _typeMeta), | ||||||
|       ); |       ); | ||||||
|     } else if (isInserting) { |  | ||||||
|       context.missing(_postMeta); |  | ||||||
|     } |     } | ||||||
|     if (data.containsKey('last_modified')) { |     if (data.containsKey('last_modified')) { | ||||||
|       context.handle( |       context.handle( | ||||||
| @@ -642,6 +731,14 @@ class $PostDraftsTable extends PostDrafts | |||||||
|     } else if (isInserting) { |     } else if (isInserting) { | ||||||
|       context.missing(_lastModifiedMeta); |       context.missing(_lastModifiedMeta); | ||||||
|     } |     } | ||||||
|  |     if (data.containsKey('post_data')) { | ||||||
|  |       context.handle( | ||||||
|  |         _postDataMeta, | ||||||
|  |         postData.isAcceptableOrUnknown(data['post_data']!, _postDataMeta), | ||||||
|  |       ); | ||||||
|  |     } else if (isInserting) { | ||||||
|  |       context.missing(_postDataMeta); | ||||||
|  |     } | ||||||
|     return context; |     return context; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -656,16 +753,38 @@ class $PostDraftsTable extends PostDrafts | |||||||
|             DriftSqlType.string, |             DriftSqlType.string, | ||||||
|             data['${effectivePrefix}id'], |             data['${effectivePrefix}id'], | ||||||
|           )!, |           )!, | ||||||
|       post: |       title: attachedDatabase.typeMapping.read( | ||||||
|  |         DriftSqlType.string, | ||||||
|  |         data['${effectivePrefix}title'], | ||||||
|  |       ), | ||||||
|  |       description: attachedDatabase.typeMapping.read( | ||||||
|  |         DriftSqlType.string, | ||||||
|  |         data['${effectivePrefix}description'], | ||||||
|  |       ), | ||||||
|  |       content: attachedDatabase.typeMapping.read( | ||||||
|  |         DriftSqlType.string, | ||||||
|  |         data['${effectivePrefix}content'], | ||||||
|  |       ), | ||||||
|  |       visibility: | ||||||
|           attachedDatabase.typeMapping.read( |           attachedDatabase.typeMapping.read( | ||||||
|             DriftSqlType.string, |             DriftSqlType.int, | ||||||
|             data['${effectivePrefix}post'], |             data['${effectivePrefix}visibility'], | ||||||
|  |           )!, | ||||||
|  |       type: | ||||||
|  |           attachedDatabase.typeMapping.read( | ||||||
|  |             DriftSqlType.int, | ||||||
|  |             data['${effectivePrefix}type'], | ||||||
|           )!, |           )!, | ||||||
|       lastModified: |       lastModified: | ||||||
|           attachedDatabase.typeMapping.read( |           attachedDatabase.typeMapping.read( | ||||||
|             DriftSqlType.dateTime, |             DriftSqlType.dateTime, | ||||||
|             data['${effectivePrefix}last_modified'], |             data['${effectivePrefix}last_modified'], | ||||||
|           )!, |           )!, | ||||||
|  |       postData: | ||||||
|  |           attachedDatabase.typeMapping.read( | ||||||
|  |             DriftSqlType.string, | ||||||
|  |             data['${effectivePrefix}post_data'], | ||||||
|  |           )!, | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -677,27 +796,60 @@ class $PostDraftsTable extends PostDrafts | |||||||
|  |  | ||||||
| class PostDraft extends DataClass implements Insertable<PostDraft> { | class PostDraft extends DataClass implements Insertable<PostDraft> { | ||||||
|   final String id; |   final String id; | ||||||
|   final String post; |   final String? title; | ||||||
|  |   final String? description; | ||||||
|  |   final String? content; | ||||||
|  |   final int visibility; | ||||||
|  |   final int type; | ||||||
|   final DateTime lastModified; |   final DateTime lastModified; | ||||||
|  |   final String postData; | ||||||
|   const PostDraft({ |   const PostDraft({ | ||||||
|     required this.id, |     required this.id, | ||||||
|     required this.post, |     this.title, | ||||||
|  |     this.description, | ||||||
|  |     this.content, | ||||||
|  |     required this.visibility, | ||||||
|  |     required this.type, | ||||||
|     required this.lastModified, |     required this.lastModified, | ||||||
|  |     required this.postData, | ||||||
|   }); |   }); | ||||||
|   @override |   @override | ||||||
|   Map<String, Expression> toColumns(bool nullToAbsent) { |   Map<String, Expression> toColumns(bool nullToAbsent) { | ||||||
|     final map = <String, Expression>{}; |     final map = <String, Expression>{}; | ||||||
|     map['id'] = Variable<String>(id); |     map['id'] = Variable<String>(id); | ||||||
|     map['post'] = Variable<String>(post); |     if (!nullToAbsent || title != null) { | ||||||
|  |       map['title'] = Variable<String>(title); | ||||||
|  |     } | ||||||
|  |     if (!nullToAbsent || description != null) { | ||||||
|  |       map['description'] = Variable<String>(description); | ||||||
|  |     } | ||||||
|  |     if (!nullToAbsent || content != null) { | ||||||
|  |       map['content'] = Variable<String>(content); | ||||||
|  |     } | ||||||
|  |     map['visibility'] = Variable<int>(visibility); | ||||||
|  |     map['type'] = Variable<int>(type); | ||||||
|     map['last_modified'] = Variable<DateTime>(lastModified); |     map['last_modified'] = Variable<DateTime>(lastModified); | ||||||
|  |     map['post_data'] = Variable<String>(postData); | ||||||
|     return map; |     return map; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   PostDraftsCompanion toCompanion(bool nullToAbsent) { |   PostDraftsCompanion toCompanion(bool nullToAbsent) { | ||||||
|     return PostDraftsCompanion( |     return PostDraftsCompanion( | ||||||
|       id: Value(id), |       id: Value(id), | ||||||
|       post: Value(post), |       title: | ||||||
|  |           title == null && nullToAbsent ? const Value.absent() : Value(title), | ||||||
|  |       description: | ||||||
|  |           description == null && nullToAbsent | ||||||
|  |               ? const Value.absent() | ||||||
|  |               : Value(description), | ||||||
|  |       content: | ||||||
|  |           content == null && nullToAbsent | ||||||
|  |               ? const Value.absent() | ||||||
|  |               : Value(content), | ||||||
|  |       visibility: Value(visibility), | ||||||
|  |       type: Value(type), | ||||||
|       lastModified: Value(lastModified), |       lastModified: Value(lastModified), | ||||||
|  |       postData: Value(postData), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -708,8 +860,13 @@ class PostDraft extends DataClass implements Insertable<PostDraft> { | |||||||
|     serializer ??= driftRuntimeOptions.defaultSerializer; |     serializer ??= driftRuntimeOptions.defaultSerializer; | ||||||
|     return PostDraft( |     return PostDraft( | ||||||
|       id: serializer.fromJson<String>(json['id']), |       id: serializer.fromJson<String>(json['id']), | ||||||
|       post: serializer.fromJson<String>(json['post']), |       title: serializer.fromJson<String?>(json['title']), | ||||||
|  |       description: serializer.fromJson<String?>(json['description']), | ||||||
|  |       content: serializer.fromJson<String?>(json['content']), | ||||||
|  |       visibility: serializer.fromJson<int>(json['visibility']), | ||||||
|  |       type: serializer.fromJson<int>(json['type']), | ||||||
|       lastModified: serializer.fromJson<DateTime>(json['lastModified']), |       lastModified: serializer.fromJson<DateTime>(json['lastModified']), | ||||||
|  |       postData: serializer.fromJson<String>(json['postData']), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|   @override |   @override | ||||||
| @@ -717,25 +874,50 @@ class PostDraft extends DataClass implements Insertable<PostDraft> { | |||||||
|     serializer ??= driftRuntimeOptions.defaultSerializer; |     serializer ??= driftRuntimeOptions.defaultSerializer; | ||||||
|     return <String, dynamic>{ |     return <String, dynamic>{ | ||||||
|       'id': serializer.toJson<String>(id), |       'id': serializer.toJson<String>(id), | ||||||
|       'post': serializer.toJson<String>(post), |       'title': serializer.toJson<String?>(title), | ||||||
|  |       'description': serializer.toJson<String?>(description), | ||||||
|  |       'content': serializer.toJson<String?>(content), | ||||||
|  |       'visibility': serializer.toJson<int>(visibility), | ||||||
|  |       'type': serializer.toJson<int>(type), | ||||||
|       'lastModified': serializer.toJson<DateTime>(lastModified), |       'lastModified': serializer.toJson<DateTime>(lastModified), | ||||||
|  |       'postData': serializer.toJson<String>(postData), | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   PostDraft copyWith({String? id, String? post, DateTime? lastModified}) => |   PostDraft copyWith({ | ||||||
|       PostDraft( |     String? id, | ||||||
|         id: id ?? this.id, |     Value<String?> title = const Value.absent(), | ||||||
|         post: post ?? this.post, |     Value<String?> description = const Value.absent(), | ||||||
|         lastModified: lastModified ?? this.lastModified, |     Value<String?> content = const Value.absent(), | ||||||
|       ); |     int? visibility, | ||||||
|  |     int? type, | ||||||
|  |     DateTime? lastModified, | ||||||
|  |     String? postData, | ||||||
|  |   }) => PostDraft( | ||||||
|  |     id: id ?? this.id, | ||||||
|  |     title: title.present ? title.value : this.title, | ||||||
|  |     description: description.present ? description.value : this.description, | ||||||
|  |     content: content.present ? content.value : this.content, | ||||||
|  |     visibility: visibility ?? this.visibility, | ||||||
|  |     type: type ?? this.type, | ||||||
|  |     lastModified: lastModified ?? this.lastModified, | ||||||
|  |     postData: postData ?? this.postData, | ||||||
|  |   ); | ||||||
|   PostDraft copyWithCompanion(PostDraftsCompanion data) { |   PostDraft copyWithCompanion(PostDraftsCompanion data) { | ||||||
|     return PostDraft( |     return PostDraft( | ||||||
|       id: data.id.present ? data.id.value : this.id, |       id: data.id.present ? data.id.value : this.id, | ||||||
|       post: data.post.present ? data.post.value : this.post, |       title: data.title.present ? data.title.value : this.title, | ||||||
|  |       description: | ||||||
|  |           data.description.present ? data.description.value : this.description, | ||||||
|  |       content: data.content.present ? data.content.value : this.content, | ||||||
|  |       visibility: | ||||||
|  |           data.visibility.present ? data.visibility.value : this.visibility, | ||||||
|  |       type: data.type.present ? data.type.value : this.type, | ||||||
|       lastModified: |       lastModified: | ||||||
|           data.lastModified.present |           data.lastModified.present | ||||||
|               ? data.lastModified.value |               ? data.lastModified.value | ||||||
|               : this.lastModified, |               : this.lastModified, | ||||||
|  |       postData: data.postData.present ? data.postData.value : this.postData, | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -743,66 +925,120 @@ class PostDraft extends DataClass implements Insertable<PostDraft> { | |||||||
|   String toString() { |   String toString() { | ||||||
|     return (StringBuffer('PostDraft(') |     return (StringBuffer('PostDraft(') | ||||||
|           ..write('id: $id, ') |           ..write('id: $id, ') | ||||||
|           ..write('post: $post, ') |           ..write('title: $title, ') | ||||||
|           ..write('lastModified: $lastModified') |           ..write('description: $description, ') | ||||||
|  |           ..write('content: $content, ') | ||||||
|  |           ..write('visibility: $visibility, ') | ||||||
|  |           ..write('type: $type, ') | ||||||
|  |           ..write('lastModified: $lastModified, ') | ||||||
|  |           ..write('postData: $postData') | ||||||
|           ..write(')')) |           ..write(')')) | ||||||
|         .toString(); |         .toString(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   int get hashCode => Object.hash(id, post, lastModified); |   int get hashCode => Object.hash( | ||||||
|  |     id, | ||||||
|  |     title, | ||||||
|  |     description, | ||||||
|  |     content, | ||||||
|  |     visibility, | ||||||
|  |     type, | ||||||
|  |     lastModified, | ||||||
|  |     postData, | ||||||
|  |   ); | ||||||
|   @override |   @override | ||||||
|   bool operator ==(Object other) => |   bool operator ==(Object other) => | ||||||
|       identical(this, other) || |       identical(this, other) || | ||||||
|       (other is PostDraft && |       (other is PostDraft && | ||||||
|           other.id == this.id && |           other.id == this.id && | ||||||
|           other.post == this.post && |           other.title == this.title && | ||||||
|           other.lastModified == this.lastModified); |           other.description == this.description && | ||||||
|  |           other.content == this.content && | ||||||
|  |           other.visibility == this.visibility && | ||||||
|  |           other.type == this.type && | ||||||
|  |           other.lastModified == this.lastModified && | ||||||
|  |           other.postData == this.postData); | ||||||
| } | } | ||||||
|  |  | ||||||
| class PostDraftsCompanion extends UpdateCompanion<PostDraft> { | class PostDraftsCompanion extends UpdateCompanion<PostDraft> { | ||||||
|   final Value<String> id; |   final Value<String> id; | ||||||
|   final Value<String> post; |   final Value<String?> title; | ||||||
|  |   final Value<String?> description; | ||||||
|  |   final Value<String?> content; | ||||||
|  |   final Value<int> visibility; | ||||||
|  |   final Value<int> type; | ||||||
|   final Value<DateTime> lastModified; |   final Value<DateTime> lastModified; | ||||||
|  |   final Value<String> postData; | ||||||
|   final Value<int> rowid; |   final Value<int> rowid; | ||||||
|   const PostDraftsCompanion({ |   const PostDraftsCompanion({ | ||||||
|     this.id = const Value.absent(), |     this.id = const Value.absent(), | ||||||
|     this.post = const Value.absent(), |     this.title = const Value.absent(), | ||||||
|  |     this.description = const Value.absent(), | ||||||
|  |     this.content = const Value.absent(), | ||||||
|  |     this.visibility = const Value.absent(), | ||||||
|  |     this.type = const Value.absent(), | ||||||
|     this.lastModified = const Value.absent(), |     this.lastModified = const Value.absent(), | ||||||
|  |     this.postData = const Value.absent(), | ||||||
|     this.rowid = const Value.absent(), |     this.rowid = const Value.absent(), | ||||||
|   }); |   }); | ||||||
|   PostDraftsCompanion.insert({ |   PostDraftsCompanion.insert({ | ||||||
|     required String id, |     required String id, | ||||||
|     required String post, |     this.title = const Value.absent(), | ||||||
|  |     this.description = const Value.absent(), | ||||||
|  |     this.content = const Value.absent(), | ||||||
|  |     this.visibility = const Value.absent(), | ||||||
|  |     this.type = const Value.absent(), | ||||||
|     required DateTime lastModified, |     required DateTime lastModified, | ||||||
|  |     required String postData, | ||||||
|     this.rowid = const Value.absent(), |     this.rowid = const Value.absent(), | ||||||
|   }) : id = Value(id), |   }) : id = Value(id), | ||||||
|        post = Value(post), |        lastModified = Value(lastModified), | ||||||
|        lastModified = Value(lastModified); |        postData = Value(postData); | ||||||
|   static Insertable<PostDraft> custom({ |   static Insertable<PostDraft> custom({ | ||||||
|     Expression<String>? id, |     Expression<String>? id, | ||||||
|     Expression<String>? post, |     Expression<String>? title, | ||||||
|  |     Expression<String>? description, | ||||||
|  |     Expression<String>? content, | ||||||
|  |     Expression<int>? visibility, | ||||||
|  |     Expression<int>? type, | ||||||
|     Expression<DateTime>? lastModified, |     Expression<DateTime>? lastModified, | ||||||
|  |     Expression<String>? postData, | ||||||
|     Expression<int>? rowid, |     Expression<int>? rowid, | ||||||
|   }) { |   }) { | ||||||
|     return RawValuesInsertable({ |     return RawValuesInsertable({ | ||||||
|       if (id != null) 'id': id, |       if (id != null) 'id': id, | ||||||
|       if (post != null) 'post': post, |       if (title != null) 'title': title, | ||||||
|  |       if (description != null) 'description': description, | ||||||
|  |       if (content != null) 'content': content, | ||||||
|  |       if (visibility != null) 'visibility': visibility, | ||||||
|  |       if (type != null) 'type': type, | ||||||
|       if (lastModified != null) 'last_modified': lastModified, |       if (lastModified != null) 'last_modified': lastModified, | ||||||
|  |       if (postData != null) 'post_data': postData, | ||||||
|       if (rowid != null) 'rowid': rowid, |       if (rowid != null) 'rowid': rowid, | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   PostDraftsCompanion copyWith({ |   PostDraftsCompanion copyWith({ | ||||||
|     Value<String>? id, |     Value<String>? id, | ||||||
|     Value<String>? post, |     Value<String?>? title, | ||||||
|  |     Value<String?>? description, | ||||||
|  |     Value<String?>? content, | ||||||
|  |     Value<int>? visibility, | ||||||
|  |     Value<int>? type, | ||||||
|     Value<DateTime>? lastModified, |     Value<DateTime>? lastModified, | ||||||
|  |     Value<String>? postData, | ||||||
|     Value<int>? rowid, |     Value<int>? rowid, | ||||||
|   }) { |   }) { | ||||||
|     return PostDraftsCompanion( |     return PostDraftsCompanion( | ||||||
|       id: id ?? this.id, |       id: id ?? this.id, | ||||||
|       post: post ?? this.post, |       title: title ?? this.title, | ||||||
|  |       description: description ?? this.description, | ||||||
|  |       content: content ?? this.content, | ||||||
|  |       visibility: visibility ?? this.visibility, | ||||||
|  |       type: type ?? this.type, | ||||||
|       lastModified: lastModified ?? this.lastModified, |       lastModified: lastModified ?? this.lastModified, | ||||||
|  |       postData: postData ?? this.postData, | ||||||
|       rowid: rowid ?? this.rowid, |       rowid: rowid ?? this.rowid, | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| @@ -813,12 +1049,27 @@ class PostDraftsCompanion extends UpdateCompanion<PostDraft> { | |||||||
|     if (id.present) { |     if (id.present) { | ||||||
|       map['id'] = Variable<String>(id.value); |       map['id'] = Variable<String>(id.value); | ||||||
|     } |     } | ||||||
|     if (post.present) { |     if (title.present) { | ||||||
|       map['post'] = Variable<String>(post.value); |       map['title'] = Variable<String>(title.value); | ||||||
|  |     } | ||||||
|  |     if (description.present) { | ||||||
|  |       map['description'] = Variable<String>(description.value); | ||||||
|  |     } | ||||||
|  |     if (content.present) { | ||||||
|  |       map['content'] = Variable<String>(content.value); | ||||||
|  |     } | ||||||
|  |     if (visibility.present) { | ||||||
|  |       map['visibility'] = Variable<int>(visibility.value); | ||||||
|  |     } | ||||||
|  |     if (type.present) { | ||||||
|  |       map['type'] = Variable<int>(type.value); | ||||||
|     } |     } | ||||||
|     if (lastModified.present) { |     if (lastModified.present) { | ||||||
|       map['last_modified'] = Variable<DateTime>(lastModified.value); |       map['last_modified'] = Variable<DateTime>(lastModified.value); | ||||||
|     } |     } | ||||||
|  |     if (postData.present) { | ||||||
|  |       map['post_data'] = Variable<String>(postData.value); | ||||||
|  |     } | ||||||
|     if (rowid.present) { |     if (rowid.present) { | ||||||
|       map['rowid'] = Variable<int>(rowid.value); |       map['rowid'] = Variable<int>(rowid.value); | ||||||
|     } |     } | ||||||
| @@ -829,8 +1080,13 @@ class PostDraftsCompanion extends UpdateCompanion<PostDraft> { | |||||||
|   String toString() { |   String toString() { | ||||||
|     return (StringBuffer('PostDraftsCompanion(') |     return (StringBuffer('PostDraftsCompanion(') | ||||||
|           ..write('id: $id, ') |           ..write('id: $id, ') | ||||||
|           ..write('post: $post, ') |           ..write('title: $title, ') | ||||||
|  |           ..write('description: $description, ') | ||||||
|  |           ..write('content: $content, ') | ||||||
|  |           ..write('visibility: $visibility, ') | ||||||
|  |           ..write('type: $type, ') | ||||||
|           ..write('lastModified: $lastModified, ') |           ..write('lastModified: $lastModified, ') | ||||||
|  |           ..write('postData: $postData, ') | ||||||
|           ..write('rowid: $rowid') |           ..write('rowid: $rowid') | ||||||
|           ..write(')')) |           ..write(')')) | ||||||
|         .toString(); |         .toString(); | ||||||
| @@ -1140,15 +1396,25 @@ typedef $$ChatMessagesTableProcessedTableManager = | |||||||
| typedef $$PostDraftsTableCreateCompanionBuilder = | typedef $$PostDraftsTableCreateCompanionBuilder = | ||||||
|     PostDraftsCompanion Function({ |     PostDraftsCompanion Function({ | ||||||
|       required String id, |       required String id, | ||||||
|       required String post, |       Value<String?> title, | ||||||
|  |       Value<String?> description, | ||||||
|  |       Value<String?> content, | ||||||
|  |       Value<int> visibility, | ||||||
|  |       Value<int> type, | ||||||
|       required DateTime lastModified, |       required DateTime lastModified, | ||||||
|  |       required String postData, | ||||||
|       Value<int> rowid, |       Value<int> rowid, | ||||||
|     }); |     }); | ||||||
| typedef $$PostDraftsTableUpdateCompanionBuilder = | typedef $$PostDraftsTableUpdateCompanionBuilder = | ||||||
|     PostDraftsCompanion Function({ |     PostDraftsCompanion Function({ | ||||||
|       Value<String> id, |       Value<String> id, | ||||||
|       Value<String> post, |       Value<String?> title, | ||||||
|  |       Value<String?> description, | ||||||
|  |       Value<String?> content, | ||||||
|  |       Value<int> visibility, | ||||||
|  |       Value<int> type, | ||||||
|       Value<DateTime> lastModified, |       Value<DateTime> lastModified, | ||||||
|  |       Value<String> postData, | ||||||
|       Value<int> rowid, |       Value<int> rowid, | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
| @@ -1166,8 +1432,28 @@ class $$PostDraftsTableFilterComposer | |||||||
|     builder: (column) => ColumnFilters(column), |     builder: (column) => ColumnFilters(column), | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   ColumnFilters<String> get post => $composableBuilder( |   ColumnFilters<String> get title => $composableBuilder( | ||||||
|     column: $table.post, |     column: $table.title, | ||||||
|  |     builder: (column) => ColumnFilters(column), | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   ColumnFilters<String> get description => $composableBuilder( | ||||||
|  |     column: $table.description, | ||||||
|  |     builder: (column) => ColumnFilters(column), | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   ColumnFilters<String> get content => $composableBuilder( | ||||||
|  |     column: $table.content, | ||||||
|  |     builder: (column) => ColumnFilters(column), | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   ColumnFilters<int> get visibility => $composableBuilder( | ||||||
|  |     column: $table.visibility, | ||||||
|  |     builder: (column) => ColumnFilters(column), | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   ColumnFilters<int> get type => $composableBuilder( | ||||||
|  |     column: $table.type, | ||||||
|     builder: (column) => ColumnFilters(column), |     builder: (column) => ColumnFilters(column), | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
| @@ -1175,6 +1461,11 @@ class $$PostDraftsTableFilterComposer | |||||||
|     column: $table.lastModified, |     column: $table.lastModified, | ||||||
|     builder: (column) => ColumnFilters(column), |     builder: (column) => ColumnFilters(column), | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  |   ColumnFilters<String> get postData => $composableBuilder( | ||||||
|  |     column: $table.postData, | ||||||
|  |     builder: (column) => ColumnFilters(column), | ||||||
|  |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
| class $$PostDraftsTableOrderingComposer | class $$PostDraftsTableOrderingComposer | ||||||
| @@ -1191,8 +1482,28 @@ class $$PostDraftsTableOrderingComposer | |||||||
|     builder: (column) => ColumnOrderings(column), |     builder: (column) => ColumnOrderings(column), | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   ColumnOrderings<String> get post => $composableBuilder( |   ColumnOrderings<String> get title => $composableBuilder( | ||||||
|     column: $table.post, |     column: $table.title, | ||||||
|  |     builder: (column) => ColumnOrderings(column), | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   ColumnOrderings<String> get description => $composableBuilder( | ||||||
|  |     column: $table.description, | ||||||
|  |     builder: (column) => ColumnOrderings(column), | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   ColumnOrderings<String> get content => $composableBuilder( | ||||||
|  |     column: $table.content, | ||||||
|  |     builder: (column) => ColumnOrderings(column), | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   ColumnOrderings<int> get visibility => $composableBuilder( | ||||||
|  |     column: $table.visibility, | ||||||
|  |     builder: (column) => ColumnOrderings(column), | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   ColumnOrderings<int> get type => $composableBuilder( | ||||||
|  |     column: $table.type, | ||||||
|     builder: (column) => ColumnOrderings(column), |     builder: (column) => ColumnOrderings(column), | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
| @@ -1200,6 +1511,11 @@ class $$PostDraftsTableOrderingComposer | |||||||
|     column: $table.lastModified, |     column: $table.lastModified, | ||||||
|     builder: (column) => ColumnOrderings(column), |     builder: (column) => ColumnOrderings(column), | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  |   ColumnOrderings<String> get postData => $composableBuilder( | ||||||
|  |     column: $table.postData, | ||||||
|  |     builder: (column) => ColumnOrderings(column), | ||||||
|  |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
| class $$PostDraftsTableAnnotationComposer | class $$PostDraftsTableAnnotationComposer | ||||||
| @@ -1214,13 +1530,32 @@ class $$PostDraftsTableAnnotationComposer | |||||||
|   GeneratedColumn<String> get id => |   GeneratedColumn<String> get id => | ||||||
|       $composableBuilder(column: $table.id, builder: (column) => column); |       $composableBuilder(column: $table.id, builder: (column) => column); | ||||||
|  |  | ||||||
|   GeneratedColumn<String> get post => |   GeneratedColumn<String> get title => | ||||||
|       $composableBuilder(column: $table.post, builder: (column) => column); |       $composableBuilder(column: $table.title, builder: (column) => column); | ||||||
|  |  | ||||||
|  |   GeneratedColumn<String> get description => $composableBuilder( | ||||||
|  |     column: $table.description, | ||||||
|  |     builder: (column) => column, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   GeneratedColumn<String> get content => | ||||||
|  |       $composableBuilder(column: $table.content, builder: (column) => column); | ||||||
|  |  | ||||||
|  |   GeneratedColumn<int> get visibility => $composableBuilder( | ||||||
|  |     column: $table.visibility, | ||||||
|  |     builder: (column) => column, | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   GeneratedColumn<int> get type => | ||||||
|  |       $composableBuilder(column: $table.type, builder: (column) => column); | ||||||
|  |  | ||||||
|   GeneratedColumn<DateTime> get lastModified => $composableBuilder( |   GeneratedColumn<DateTime> get lastModified => $composableBuilder( | ||||||
|     column: $table.lastModified, |     column: $table.lastModified, | ||||||
|     builder: (column) => column, |     builder: (column) => column, | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  |   GeneratedColumn<String> get postData => | ||||||
|  |       $composableBuilder(column: $table.postData, builder: (column) => column); | ||||||
| } | } | ||||||
|  |  | ||||||
| class $$PostDraftsTableTableManager | class $$PostDraftsTableTableManager | ||||||
| @@ -1255,25 +1590,45 @@ class $$PostDraftsTableTableManager | |||||||
|           updateCompanionCallback: |           updateCompanionCallback: | ||||||
|               ({ |               ({ | ||||||
|                 Value<String> id = const Value.absent(), |                 Value<String> id = const Value.absent(), | ||||||
|                 Value<String> post = const Value.absent(), |                 Value<String?> title = const Value.absent(), | ||||||
|  |                 Value<String?> description = const Value.absent(), | ||||||
|  |                 Value<String?> content = const Value.absent(), | ||||||
|  |                 Value<int> visibility = const Value.absent(), | ||||||
|  |                 Value<int> type = const Value.absent(), | ||||||
|                 Value<DateTime> lastModified = const Value.absent(), |                 Value<DateTime> lastModified = const Value.absent(), | ||||||
|  |                 Value<String> postData = const Value.absent(), | ||||||
|                 Value<int> rowid = const Value.absent(), |                 Value<int> rowid = const Value.absent(), | ||||||
|               }) => PostDraftsCompanion( |               }) => PostDraftsCompanion( | ||||||
|                 id: id, |                 id: id, | ||||||
|                 post: post, |                 title: title, | ||||||
|  |                 description: description, | ||||||
|  |                 content: content, | ||||||
|  |                 visibility: visibility, | ||||||
|  |                 type: type, | ||||||
|                 lastModified: lastModified, |                 lastModified: lastModified, | ||||||
|  |                 postData: postData, | ||||||
|                 rowid: rowid, |                 rowid: rowid, | ||||||
|               ), |               ), | ||||||
|           createCompanionCallback: |           createCompanionCallback: | ||||||
|               ({ |               ({ | ||||||
|                 required String id, |                 required String id, | ||||||
|                 required String post, |                 Value<String?> title = const Value.absent(), | ||||||
|  |                 Value<String?> description = const Value.absent(), | ||||||
|  |                 Value<String?> content = const Value.absent(), | ||||||
|  |                 Value<int> visibility = const Value.absent(), | ||||||
|  |                 Value<int> type = const Value.absent(), | ||||||
|                 required DateTime lastModified, |                 required DateTime lastModified, | ||||||
|  |                 required String postData, | ||||||
|                 Value<int> rowid = const Value.absent(), |                 Value<int> rowid = const Value.absent(), | ||||||
|               }) => PostDraftsCompanion.insert( |               }) => PostDraftsCompanion.insert( | ||||||
|                 id: id, |                 id: id, | ||||||
|                 post: post, |                 title: title, | ||||||
|  |                 description: description, | ||||||
|  |                 content: content, | ||||||
|  |                 visibility: visibility, | ||||||
|  |                 type: type, | ||||||
|                 lastModified: lastModified, |                 lastModified: lastModified, | ||||||
|  |                 postData: postData, | ||||||
|                 rowid: rowid, |                 rowid: rowid, | ||||||
|               ), |               ), | ||||||
|           withReferenceMapper: |           withReferenceMapper: | ||||||
|   | |||||||
| @@ -128,14 +128,6 @@ class ArticleComposeScreen extends HookConsumerWidget { | |||||||
|       return null; |       return null; | ||||||
|     }, []); |     }, []); | ||||||
|  |  | ||||||
|     // Auto-save cleanup |  | ||||||
|     useEffect(() { |  | ||||||
|       return () { |  | ||||||
|         state.stopAutoSave(); |  | ||||||
|         ComposeLogic.dispose(state); |  | ||||||
|       }; |  | ||||||
|     }, [state]); |  | ||||||
|  |  | ||||||
|     // Helper methods |     // Helper methods | ||||||
|     void showSettingsSheet() { |     void showSettingsSheet() { | ||||||
|       showModalBottomSheet( |       showModalBottomSheet( | ||||||
| @@ -182,6 +174,12 @@ class ArticleComposeScreen extends HookConsumerWidget { | |||||||
|                           MarkdownTextContent( |                           MarkdownTextContent( | ||||||
|                             content: contentValue.text, |                             content: contentValue.text, | ||||||
|                             textStyle: theme.textTheme.bodyMedium, |                             textStyle: theme.textTheme.bodyMedium, | ||||||
|  |                             attachments: | ||||||
|  |                                 state.attachments.value | ||||||
|  |                                     .where((e) => e.isOnCloud) | ||||||
|  |                                     .map((e) => e.data) | ||||||
|  |                                     .cast<SnCloudFile>() | ||||||
|  |                                     .toList(), | ||||||
|                           ), |                           ), | ||||||
|                       ], |                       ], | ||||||
|                     ); |                     ); | ||||||
| @@ -268,7 +266,7 @@ class ArticleComposeScreen extends HookConsumerWidget { | |||||||
|                 child: KeyboardListener( |                 child: KeyboardListener( | ||||||
|                   focusNode: FocusNode(), |                   focusNode: FocusNode(), | ||||||
|                   onKeyEvent: |                   onKeyEvent: | ||||||
|                       (event) => _handleKeyPress( |                       (event) => ComposeLogic.handleKeyPress( | ||||||
|                         event, |                         event, | ||||||
|                         state, |                         state, | ||||||
|                         ref, |                         ref, | ||||||
| @@ -511,38 +509,4 @@ class ArticleComposeScreen extends HookConsumerWidget { | |||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Helper method to handle keyboard shortcuts |  | ||||||
|   void _handleKeyPress( |  | ||||||
|     KeyEvent event, |  | ||||||
|     ComposeState state, |  | ||||||
|     WidgetRef ref, |  | ||||||
|     BuildContext context, { |  | ||||||
|     SnPost? originalPost, |  | ||||||
|   }) { |  | ||||||
|     if (event is! RawKeyDownEvent) return; |  | ||||||
|  |  | ||||||
|     final isPaste = event.logicalKey == LogicalKeyboardKey.keyV; |  | ||||||
|     final isSave = event.logicalKey == LogicalKeyboardKey.keyS; |  | ||||||
|     final isModifierPressed = |  | ||||||
|         HardwareKeyboard.instance.isMetaPressed || |  | ||||||
|         HardwareKeyboard.instance.isControlPressed; |  | ||||||
|     final isSubmit = event.logicalKey == LogicalKeyboardKey.enter; |  | ||||||
|  |  | ||||||
|     if (isPaste && isModifierPressed) { |  | ||||||
|       ComposeLogic.handlePaste(state); |  | ||||||
|     } else if (isSave && isModifierPressed) { |  | ||||||
|       ComposeLogic.saveDraft(ref, state); |  | ||||||
|       ComposeLogic.saveDraft(ref, state); |  | ||||||
|     } else if (isSubmit && isModifierPressed && !state.submitting.value) { |  | ||||||
|       ComposeLogic.performAction( |  | ||||||
|         ref, |  | ||||||
|         state, |  | ||||||
|         context, |  | ||||||
|         originalPost: originalPost, |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Helper method to save article draft |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -39,8 +39,13 @@ class ComposeStorageNotifier extends _$ComposeStorageNotifier { | |||||||
|       await database.addPostDraft( |       await database.addPostDraft( | ||||||
|         PostDraftsCompanion( |         PostDraftsCompanion( | ||||||
|           id: Value(updatedDraft.id), |           id: Value(updatedDraft.id), | ||||||
|           post: Value(jsonEncode(updatedDraft.toJson())), |           title: Value(updatedDraft.title), | ||||||
|  |           description: Value(updatedDraft.description), | ||||||
|  |           content: Value(updatedDraft.content), | ||||||
|  |           visibility: Value(updatedDraft.visibility), | ||||||
|  |           type: Value(updatedDraft.type), | ||||||
|           lastModified: Value(updatedDraft.updatedAt ?? DateTime.now()), |           lastModified: Value(updatedDraft.updatedAt ?? DateTime.now()), | ||||||
|  |           postData: Value(jsonEncode(updatedDraft.toJson())), | ||||||
|         ), |         ), | ||||||
|       ); |       ); | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ part of 'compose_storage_db.dart'; | |||||||
| // ************************************************************************** | // ************************************************************************** | ||||||
|  |  | ||||||
| String _$composeStorageNotifierHash() => | String _$composeStorageNotifierHash() => | ||||||
|     r'4ab4dce85d0a961f096dc3b11505f8f0964dee9d'; |     r'8baf17aa06b6f69641c20645ba8a3dfe01c97f8c'; | ||||||
|  |  | ||||||
| /// See also [ComposeStorageNotifier]. | /// See also [ComposeStorageNotifier]. | ||||||
| @ProviderFor(ComposeStorageNotifier) | @ProviderFor(ComposeStorageNotifier) | ||||||
|   | |||||||
| @@ -173,10 +173,6 @@ class ComposeLogic { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     try { |     try { | ||||||
|       if (state._autoSaveTimer == null) { |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       // Upload any local attachments first |       // Upload any local attachments first | ||||||
|       final baseUrl = ref.watch(serverUrlProvider); |       final baseUrl = ref.watch(serverUrlProvider); | ||||||
|       final token = await getToken(ref.watch(tokenProvider)); |       final token = await getToken(ref.watch(tokenProvider)); | ||||||
| @@ -284,10 +280,6 @@ class ComposeLogic { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     try { |     try { | ||||||
|       if (state._autoSaveTimer == null) { |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       final draft = SnPost( |       final draft = SnPost( | ||||||
|         id: state.draftId, |         id: state.draftId, | ||||||
|         title: state.titleController.text, |         title: state.titleController.text, | ||||||
|   | |||||||
| @@ -34,7 +34,7 @@ class ComposeToolbar extends HookConsumerWidget { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     void saveDraft() { |     void saveDraft() { | ||||||
|       ComposeLogic.saveDraft(ref, state); |       ComposeLogic.saveDraftManually(ref, state, context); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void pickPoll() { |     void pickPoll() { | ||||||
|   | |||||||
| @@ -16,138 +16,145 @@ class DraftManagerSheet extends HookConsumerWidget { | |||||||
|   Widget build(BuildContext context, WidgetRef ref) { |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|     final theme = Theme.of(context); |     final theme = Theme.of(context); | ||||||
|     final colorScheme = theme.colorScheme; |     final colorScheme = theme.colorScheme; | ||||||
|     final isLoading = useState(true); |     final searchController = useTextEditingController(); | ||||||
|  |     final searchQuery = useState(''); | ||||||
|  |  | ||||||
|     final drafts = ref.watch(composeStorageNotifierProvider); |     final drafts = ref.watch(composeStorageNotifierProvider); | ||||||
|  |  | ||||||
|     // Track loading state based on drafts being loaded |     // Search functionality | ||||||
|     useEffect(() { |     final filteredDrafts = useMemoized(() { | ||||||
|       // Set loading to false after drafts are loaded |       if (searchQuery.value.isEmpty) { | ||||||
|       // We consider drafts loaded when the provider has been initialized |         return drafts.values.toList() | ||||||
|       Future.microtask(() { |           ..sort((a, b) => b.updatedAt!.compareTo(a.updatedAt!)); | ||||||
|         if (isLoading.value) { |       } | ||||||
|           isLoading.value = false; |  | ||||||
|         } |  | ||||||
|       }); |  | ||||||
|       return null; |  | ||||||
|     }, [drafts]); |  | ||||||
|  |  | ||||||
|     final sortedDrafts = useMemoized( |       final query = searchQuery.value.toLowerCase(); | ||||||
|       () { |       return drafts.values.where((draft) { | ||||||
|         final draftList = drafts.values.toList(); |           return (draft.title?.toLowerCase().contains(query) ?? false) || | ||||||
|         draftList.sort((a, b) => b.updatedAt!.compareTo(a.updatedAt!)); |               (draft.description?.toLowerCase().contains(query) ?? false) || | ||||||
|         return draftList; |               (draft.content?.toLowerCase().contains(query) ?? false); | ||||||
|       }, |         }).toList() | ||||||
|       [ |         ..sort((a, b) => b.updatedAt!.compareTo(a.updatedAt!)); | ||||||
|         drafts.length, |     }, [drafts, searchQuery.value]); | ||||||
|         drafts.values.map((e) => e.updatedAt!.millisecondsSinceEpoch).join(), |  | ||||||
|       ], |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     return SheetScaffold( |     return SheetScaffold( | ||||||
|       titleText: 'drafts'.tr(), |       titleText: 'drafts'.tr(), | ||||||
|       child: |       child: Column( | ||||||
|           isLoading.value |         children: [ | ||||||
|               ? const Center(child: CircularProgressIndicator()) |           // Search bar | ||||||
|               : Column( |           Padding( | ||||||
|                 children: [ |             padding: const EdgeInsets.all(16), | ||||||
|                   if (sortedDrafts.isEmpty) |             child: TextField( | ||||||
|                     Expanded( |               controller: searchController, | ||||||
|                       child: Center( |               decoration: InputDecoration( | ||||||
|                         child: Column( |                 hintText: 'searchDrafts'.tr(), | ||||||
|                           mainAxisAlignment: MainAxisAlignment.center, |                 prefixIcon: const Icon(Symbols.search), | ||||||
|                           children: [ |                 border: OutlineInputBorder( | ||||||
|                             Icon( |                   borderRadius: BorderRadius.circular(12), | ||||||
|                               Symbols.draft, |                 ), | ||||||
|                               size: 64, |                 contentPadding: const EdgeInsets.symmetric( | ||||||
|                               color: colorScheme.onSurface.withOpacity(0.3), |                   horizontal: 16, | ||||||
|                             ), |                   vertical: 12, | ||||||
|                             const Gap(16), |                 ), | ||||||
|                             Text( |               ), | ||||||
|                               'noDrafts'.tr(), |               onChanged: (value) => searchQuery.value = value, | ||||||
|                               style: theme.textTheme.bodyLarge?.copyWith( |             ), | ||||||
|                                 color: colorScheme.onSurface.withOpacity(0.6), |           ), | ||||||
|                               ), |  | ||||||
|                             ), |  | ||||||
|                           ], |  | ||||||
|                         ), |  | ||||||
|                       ), |  | ||||||
|                     ) |  | ||||||
|                   else |  | ||||||
|                     Expanded( |  | ||||||
|                       child: ListView.builder( |  | ||||||
|                         itemCount: sortedDrafts.length, |  | ||||||
|                         itemBuilder: (context, index) { |  | ||||||
|                           final draft = sortedDrafts[index]; |  | ||||||
|                           return _DraftItem( |  | ||||||
|                             draft: draft, |  | ||||||
|                             onTap: () { |  | ||||||
|                               Navigator.of(context).pop(); |  | ||||||
|                               onDraftSelected?.call(draft.id); |  | ||||||
|                             }, |  | ||||||
|                             onDelete: () async { |  | ||||||
|                               await ref |  | ||||||
|                                   .read(composeStorageNotifierProvider.notifier) |  | ||||||
|                                   .deleteDraft(draft.id); |  | ||||||
|                             }, |  | ||||||
|                           ); |  | ||||||
|                         }, |  | ||||||
|                       ), |  | ||||||
|                     ), |  | ||||||
|                   if (sortedDrafts.isNotEmpty) ...[ |  | ||||||
|                     const Divider(), |  | ||||||
|                     Padding( |  | ||||||
|                       padding: const EdgeInsets.all(16), |  | ||||||
|                       child: Row( |  | ||||||
|                         children: [ |  | ||||||
|                           Expanded( |  | ||||||
|                             child: OutlinedButton.icon( |  | ||||||
|                               onPressed: () async { |  | ||||||
|                                 final confirmed = await showDialog<bool>( |  | ||||||
|                                   context: context, |  | ||||||
|                                   builder: |  | ||||||
|                                       (context) => AlertDialog( |  | ||||||
|                                         title: Text('clearAllDrafts'.tr()), |  | ||||||
|                                         content: Text( |  | ||||||
|                                           'clearAllDraftsConfirm'.tr(), |  | ||||||
|                                         ), |  | ||||||
|                                         actions: [ |  | ||||||
|                                           TextButton( |  | ||||||
|                                             onPressed: |  | ||||||
|                                                 () => Navigator.of( |  | ||||||
|                                                   context, |  | ||||||
|                                                 ).pop(false), |  | ||||||
|                                             child: Text('cancel'.tr()), |  | ||||||
|                                           ), |  | ||||||
|                                           TextButton( |  | ||||||
|                                             onPressed: |  | ||||||
|                                                 () => Navigator.of( |  | ||||||
|                                                   context, |  | ||||||
|                                                 ).pop(true), |  | ||||||
|                                             child: Text('confirm'.tr()), |  | ||||||
|                                           ), |  | ||||||
|                                         ], |  | ||||||
|                                       ), |  | ||||||
|                                 ); |  | ||||||
|  |  | ||||||
|                                 if (confirmed == true) { |           // Drafts list | ||||||
|                                   await ref |           if (filteredDrafts.isEmpty) | ||||||
|                                       .read( |             Expanded( | ||||||
|                                         composeStorageNotifierProvider.notifier, |               child: Center( | ||||||
|                                       ) |                 child: Column( | ||||||
|                                       .clearAllDrafts(); |                   mainAxisAlignment: MainAxisAlignment.center, | ||||||
|                                 } |                   children: [ | ||||||
|                               }, |                     Icon( | ||||||
|                               icon: const Icon(Symbols.delete_sweep), |                       Symbols.draft, | ||||||
|                               label: Text('clearAll'.tr()), |                       size: 64, | ||||||
|                             ), |                       color: colorScheme.onSurface.withOpacity(0.3), | ||||||
|                           ), |                     ), | ||||||
|                         ], |                     const Gap(16), | ||||||
|  |                     Text( | ||||||
|  |                       searchQuery.value.isEmpty | ||||||
|  |                           ? 'noDrafts'.tr() | ||||||
|  |                           : 'noSearchResults'.tr(), | ||||||
|  |                       style: theme.textTheme.bodyLarge?.copyWith( | ||||||
|  |                         color: colorScheme.onSurface.withOpacity(0.6), | ||||||
|                       ), |                       ), | ||||||
|                     ), |                     ), | ||||||
|                   ], |                   ], | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             ) | ||||||
|  |           else | ||||||
|  |             Expanded( | ||||||
|  |               child: ListView.builder( | ||||||
|  |                 itemCount: filteredDrafts.length, | ||||||
|  |                 itemBuilder: (context, index) { | ||||||
|  |                   final draft = filteredDrafts[index]; | ||||||
|  |                   return _DraftItem( | ||||||
|  |                     draft: draft, | ||||||
|  |                     onTap: () { | ||||||
|  |                       Navigator.of(context).pop(); | ||||||
|  |                       onDraftSelected?.call(draft.id); | ||||||
|  |                     }, | ||||||
|  |                     onDelete: () async { | ||||||
|  |                       await ref | ||||||
|  |                           .read(composeStorageNotifierProvider.notifier) | ||||||
|  |                           .deleteDraft(draft.id); | ||||||
|  |                     }, | ||||||
|  |                   ); | ||||||
|  |                 }, | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |  | ||||||
|  |           // Clear all button | ||||||
|  |           if (filteredDrafts.isNotEmpty) ...[ | ||||||
|  |             const Divider(), | ||||||
|  |             Padding( | ||||||
|  |               padding: const EdgeInsets.all(16), | ||||||
|  |               child: Row( | ||||||
|  |                 children: [ | ||||||
|  |                   Expanded( | ||||||
|  |                     child: OutlinedButton.icon( | ||||||
|  |                       onPressed: () async { | ||||||
|  |                         final confirmed = await showDialog<bool>( | ||||||
|  |                           context: context, | ||||||
|  |                           builder: | ||||||
|  |                               (context) => AlertDialog( | ||||||
|  |                                 title: Text('clearAllDrafts'.tr()), | ||||||
|  |                                 content: Text('clearAllDraftsConfirm'.tr()), | ||||||
|  |                                 actions: [ | ||||||
|  |                                   TextButton( | ||||||
|  |                                     onPressed: | ||||||
|  |                                         () => Navigator.of(context).pop(false), | ||||||
|  |                                     child: Text('cancel'.tr()), | ||||||
|  |                                   ), | ||||||
|  |                                   TextButton( | ||||||
|  |                                     onPressed: | ||||||
|  |                                         () => Navigator.of(context).pop(true), | ||||||
|  |                                     child: Text('confirm'.tr()), | ||||||
|  |                                   ), | ||||||
|  |                                 ], | ||||||
|  |                               ), | ||||||
|  |                         ); | ||||||
|  |  | ||||||
|  |                         if (confirmed == true) { | ||||||
|  |                           await ref | ||||||
|  |                               .read(composeStorageNotifierProvider.notifier) | ||||||
|  |                               .clearAllDrafts(); | ||||||
|  |                         } | ||||||
|  |                       }, | ||||||
|  |                       icon: const Icon(Symbols.delete_sweep), | ||||||
|  |                       label: Text('clearAll'.tr()), | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|                 ], |                 ], | ||||||
|               ), |               ), | ||||||
|  |             ), | ||||||
|  |           ], | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user