From 47c31ddec2752320189112361086aacad647c145 Mon Sep 17 00:00:00 2001
From: LittleSheep <littlesheep.code@hotmail.com>
Date: Wed, 25 Jun 2025 02:15:45 +0800
Subject: [PATCH] :recycle: Refactored post draft system

---
 assets/i18n/en-US.json                  |    3 +
 assets/i18n/zh-CN.json                  |   18 +-
 assets/i18n/zh-TW.json                  |   18 +-
 lib/database/draft.dart                 |   20 +-
 lib/database/drift_db.dart              |   64 +-
 lib/database/drift_db.g.dart            | 1196 +++--------------------
 lib/models/post.dart                    |   84 +-
 lib/models/post.freezed.dart            |  100 +-
 lib/models/post.g.dart                  |   77 +-
 lib/pods/theme.dart                     |    1 +
 lib/screens/account/event_calendar.dart |    3 +-
 lib/screens/posts/compose.dart          |   26 +-
 lib/screens/posts/compose_article.dart  |   59 +-
 lib/services/compose_storage_db.dart    |  335 +------
 lib/services/compose_storage_db.g.dart  |   28 +-
 lib/widgets/post/compose_shared.dart    |  268 ++++-
 lib/widgets/post/draft_manager.dart     |  300 +++---
 lib/widgets/post/post_item.dart         |   16 +-
 lib/widgets/post/post_item_creator.dart |    4 +-
 19 files changed, 776 insertions(+), 1844 deletions(-)

diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json
index ec4051e..1528157 100644
--- a/assets/i18n/en-US.json
+++ b/assets/i18n/en-US.json
@@ -410,6 +410,8 @@
   "articleDrafts": "Article drafts",
   "postDrafts": "Post drafts",
   "saveDraft": "Save draft",
+  "draftSaved": "Draft saved",
+  "draftSaveFailed": "Failed to save draft",
   "clearAllDrafts": "Clear All Drafts",
   "clearAllDraftsConfirm": "Are you sure you want to delete all drafts? This action cannot be undone.",
   "clearAll": "Clear All",
@@ -441,6 +443,7 @@
   "contactMethodDelete": "Delete Contact",
   "contactMethodNew": "New Contact Method",
   "contactMethodContentEmpty": "Contact content cannot be empty",
+  "postContentEmpty": "Post content cannot be empty",
   "contactMethodVerificationSent": "Verification code sent to your contact method",
   "contactMethodVerificationNeeded": "The contact method is added, but not verified yet. You can verify it by tapping it and select verify.",
   "accountContactMethod": "Contact Methods",
diff --git a/assets/i18n/zh-CN.json b/assets/i18n/zh-CN.json
index 81a718e..a5d5266 100644
--- a/assets/i18n/zh-CN.json
+++ b/assets/i18n/zh-CN.json
@@ -319,5 +319,21 @@
   "processingPayment": "处理付款中...",
   "pleaseWait": "请稍候",
   "paymentFailed": "付款失败,请重试。",
-  "paymentSuccess": "付款成功完成!"
+  "paymentSuccess": "付款成功完成!",
+  "drafts": "草稿",
+  "noDrafts": "暂无草稿",
+  "articleDrafts": "文章草稿",
+  "postDrafts": "帖子草稿",
+  "saveDraft": "保存草稿",
+  "draftSaved": "草稿已保存",
+  "draftSaveFailed": "保存草稿失败",
+  "clearAllDrafts": "清空所有草稿",
+  "clearAllDraftsConfirm": "确定要删除所有草稿吗?此操作无法撤销。",
+  "clearAll": "清空全部",
+  "untitled": "无标题",
+  "noContent": "无内容",
+  "justNow": "刚刚",
+  "minutesAgo": "{} 分钟前",
+  "hoursAgo": "{} 小时前",
+  "postContentEmpty": "帖子内容不能为空"
 }
\ No newline at end of file
diff --git a/assets/i18n/zh-TW.json b/assets/i18n/zh-TW.json
index d74b8e4..3aea04e 100644
--- a/assets/i18n/zh-TW.json
+++ b/assets/i18n/zh-TW.json
@@ -334,5 +334,21 @@
   "membershipFeatureAllNova": "所有新星功能",
   "membershipFeatureExclusiveContent": "獨家內容",
   "membershipFeatureVipSupport": "VIP 支援",
-  "membershipCurrentBadge": "目前"
+  "membershipCurrentBadge": "目前",
+  "drafts": "草稿",
+  "noDrafts": "暫無草稿",
+  "articleDrafts": "文章草稿",
+  "postDrafts": "貼文草稿",
+  "saveDraft": "儲存草稿",
+  "draftSaved": "草稿已儲存",
+  "draftSaveFailed": "儲存草稿失敗",
+  "clearAllDrafts": "清空所有草稿",
+  "clearAllDraftsConfirm": "確定要刪除所有草稿嗎?此操作無法復原。",
+  "clearAll": "清空全部",
+  "untitled": "無標題",
+  "noContent": "無內容",
+  "justNow": "剛剛",
+  "minutesAgo": "{} 分鐘前",
+  "hoursAgo": "{} 小時前",
+  "postContentEmpty": "貼文內容不能為空"
 }
\ No newline at end of file
diff --git a/lib/database/draft.dart b/lib/database/draft.dart
index 95cfa52..e0fe29d 100644
--- a/lib/database/draft.dart
+++ b/lib/database/draft.dart
@@ -1,26 +1,10 @@
 import 'package:drift/drift.dart';
 
-class ComposeDrafts extends Table {
+class PostDrafts extends Table {
   TextColumn get id => text()();
-  TextColumn get title => text().withDefault(const Constant(''))();
-  TextColumn get description => text().withDefault(const Constant(''))();
-  TextColumn get content => text().withDefault(const Constant(''))();
-  TextColumn get attachmentIds => text().withDefault(const Constant('[]'))(); // JSON array as string
-  IntColumn get visibility => integer().withDefault(const Constant(0))(); // 0=public, 1=unlisted, 2=friends, 3=selected, 4=private
+  TextColumn get post => text()(); // Store SnPost model as JSON string
   DateTimeColumn get lastModified => dateTime()();
 
   @override
   Set<Column> get primaryKey => {id};
 }
-
-class ArticleDrafts extends Table {
-  TextColumn get id => text()();
-  TextColumn get title => text().withDefault(const Constant(''))();
-  TextColumn get description => text().withDefault(const Constant(''))();
-  TextColumn get content => text().withDefault(const Constant(''))();
-  IntColumn get visibility => integer().withDefault(const Constant(0))(); // 0=public, 1=unlisted, 2=friends, 3=private
-  DateTimeColumn get lastModified => dateTime()();
-
-  @override
-  Set<Column> get primaryKey => {id};
-}
\ No newline at end of file
diff --git a/lib/database/drift_db.dart b/lib/database/drift_db.dart
index 3748f6c..5963525 100644
--- a/lib/database/drift_db.dart
+++ b/lib/database/drift_db.dart
@@ -2,16 +2,17 @@ import 'dart:convert';
 import 'package:drift/drift.dart';
 import 'package:island/database/message.dart';
 import 'package:island/database/draft.dart';
+import 'package:island/models/post.dart';
 
 part 'drift_db.g.dart';
 
 // Define the database
-@DriftDatabase(tables: [ChatMessages, ComposeDrafts, ArticleDrafts])
+@DriftDatabase(tables: [ChatMessages, PostDrafts])
 class AppDatabase extends _$AppDatabase {
   AppDatabase(super.e);
 
   @override
-  int get schemaVersion => 3;
+  int get schemaVersion => 4;
 
   @override
   MigrationStrategy get migration => MigrationStrategy(
@@ -23,10 +24,9 @@ class AppDatabase extends _$AppDatabase {
         // Add isRead column with default value false
         await m.addColumn(chatMessages, chatMessages.isRead);
       }
-      if (from < 3) {
-        // Add draft tables
-        await m.createTable(composeDrafts);
-        await m.createTable(articleDrafts);
+      if (from < 4) {
+        // Drop old draft tables if they exist
+        await m.createTable(postDrafts);
       }
     },
   );
@@ -98,51 +98,23 @@ class AppDatabase extends _$AppDatabase {
     );
   }
 
-  // Methods for compose drafts
-  Future<List<ComposeDraft>> getAllComposeDrafts() {
-    return (select(composeDrafts)
-          ..orderBy([(d) => OrderingTerm.desc(d.lastModified)]))
-        .get();
+  // Methods for post drafts
+  Future<List<SnPost>> getAllPostDrafts() async {
+    final drafts = await select(postDrafts).get();
+    return drafts
+        .map((draft) => SnPost.fromJson(jsonDecode(draft.post)))
+        .toList();
   }
 
-  Future<ComposeDraft?> getComposeDraft(String id) {
-    return (select(composeDrafts)..where((d) => d.id.equals(id)))
-        .getSingleOrNull();
+  Future<void> addPostDraft(PostDraftsCompanion entry) async {
+    await into(postDrafts).insert(entry, mode: InsertMode.replace);
   }
 
-  Future<int> saveComposeDraft(ComposeDraftsCompanion draft) {
-    return into(composeDrafts).insert(draft, mode: InsertMode.insertOrReplace);
+  Future<void> deletePostDraft(String id) async {
+    await (delete(postDrafts)..where((tbl) => tbl.id.equals(id))).go();
   }
 
-  Future<int> deleteComposeDraft(String id) {
-    return (delete(composeDrafts)..where((d) => d.id.equals(id))).go();
-  }
-
-  Future<int> clearAllComposeDrafts() {
-    return delete(composeDrafts).go();
-  }
-
-  // Methods for article drafts
-  Future<List<ArticleDraft>> getAllArticleDrafts() {
-    return (select(articleDrafts)
-          ..orderBy([(d) => OrderingTerm.desc(d.lastModified)]))
-        .get();
-  }
-
-  Future<ArticleDraft?> getArticleDraft(String id) {
-    return (select(articleDrafts)..where((d) => d.id.equals(id)))
-        .getSingleOrNull();
-  }
-
-  Future<int> saveArticleDraft(ArticleDraftsCompanion draft) {
-    return into(articleDrafts).insert(draft, mode: InsertMode.insertOrReplace);
-  }
-
-  Future<int> deleteArticleDraft(String id) {
-    return (delete(articleDrafts)..where((d) => d.id.equals(id))).go();
-  }
-
-  Future<int> clearAllArticleDrafts() {
-    return delete(articleDrafts).go();
+  Future<void> clearAllPostDrafts() async {
+    await delete(postDrafts).go();
   }
 }
diff --git a/lib/database/drift_db.g.dart b/lib/database/drift_db.g.dart
index 4403baa..d26a862 100644
--- a/lib/database/drift_db.g.dart
+++ b/lib/database/drift_db.g.dart
@@ -569,12 +569,12 @@ class ChatMessagesCompanion extends UpdateCompanion<ChatMessage> {
   }
 }
 
-class $ComposeDraftsTable extends ComposeDrafts
-    with TableInfo<$ComposeDraftsTable, ComposeDraft> {
+class $PostDraftsTable extends PostDrafts
+    with TableInfo<$PostDraftsTable, PostDraft> {
   @override
   final GeneratedDatabase attachedDatabase;
   final String? _alias;
-  $ComposeDraftsTable(this.attachedDatabase, [this._alias]);
+  $PostDraftsTable(this.attachedDatabase, [this._alias]);
   static const VerificationMeta _idMeta = const VerificationMeta('id');
   @override
   late final GeneratedColumn<String> id = GeneratedColumn<String>(
@@ -584,63 +584,14 @@ class $ComposeDraftsTable extends ComposeDrafts
     type: DriftSqlType.string,
     requiredDuringInsert: true,
   );
-  static const VerificationMeta _titleMeta = const VerificationMeta('title');
+  static const VerificationMeta _postMeta = const VerificationMeta('post');
   @override
-  late final GeneratedColumn<String> title = GeneratedColumn<String>(
-    'title',
+  late final GeneratedColumn<String> post = GeneratedColumn<String>(
+    'post',
     aliasedName,
     false,
     type: DriftSqlType.string,
-    requiredDuringInsert: false,
-    defaultValue: const Constant(''),
-  );
-  static const VerificationMeta _descriptionMeta = const VerificationMeta(
-    'description',
-  );
-  @override
-  late final GeneratedColumn<String> description = GeneratedColumn<String>(
-    'description',
-    aliasedName,
-    false,
-    type: DriftSqlType.string,
-    requiredDuringInsert: false,
-    defaultValue: const Constant(''),
-  );
-  static const VerificationMeta _contentMeta = const VerificationMeta(
-    'content',
-  );
-  @override
-  late final GeneratedColumn<String> content = GeneratedColumn<String>(
-    'content',
-    aliasedName,
-    false,
-    type: DriftSqlType.string,
-    requiredDuringInsert: false,
-    defaultValue: const Constant(''),
-  );
-  static const VerificationMeta _attachmentIdsMeta = const VerificationMeta(
-    'attachmentIds',
-  );
-  @override
-  late final GeneratedColumn<String> attachmentIds = GeneratedColumn<String>(
-    'attachment_ids',
-    aliasedName,
-    false,
-    type: DriftSqlType.string,
-    requiredDuringInsert: false,
-    defaultValue: const Constant('[]'),
-  );
-  static const VerificationMeta _visibilityMeta = const VerificationMeta(
-    'visibility',
-  );
-  @override
-  late final GeneratedColumn<int> visibility = GeneratedColumn<int>(
-    'visibility',
-    aliasedName,
-    false,
-    type: DriftSqlType.int,
-    requiredDuringInsert: false,
-    defaultValue: const Constant(0),
+    requiredDuringInsert: true,
   );
   static const VerificationMeta _lastModifiedMeta = const VerificationMeta(
     'lastModified',
@@ -654,23 +605,15 @@ class $ComposeDraftsTable extends ComposeDrafts
     requiredDuringInsert: true,
   );
   @override
-  List<GeneratedColumn> get $columns => [
-    id,
-    title,
-    description,
-    content,
-    attachmentIds,
-    visibility,
-    lastModified,
-  ];
+  List<GeneratedColumn> get $columns => [id, post, lastModified];
   @override
   String get aliasedName => _alias ?? actualTableName;
   @override
   String get actualTableName => $name;
-  static const String $name = 'compose_drafts';
+  static const String $name = 'post_drafts';
   @override
   VerificationContext validateIntegrity(
-    Insertable<ComposeDraft> instance, {
+    Insertable<PostDraft> instance, {
     bool isInserting = false,
   }) {
     final context = VerificationContext();
@@ -680,41 +623,13 @@ class $ComposeDraftsTable extends ComposeDrafts
     } else if (isInserting) {
       context.missing(_idMeta);
     }
-    if (data.containsKey('title')) {
+    if (data.containsKey('post')) {
       context.handle(
-        _titleMeta,
-        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('attachment_ids')) {
-      context.handle(
-        _attachmentIdsMeta,
-        attachmentIds.isAcceptableOrUnknown(
-          data['attachment_ids']!,
-          _attachmentIdsMeta,
-        ),
-      );
-    }
-    if (data.containsKey('visibility')) {
-      context.handle(
-        _visibilityMeta,
-        visibility.isAcceptableOrUnknown(data['visibility']!, _visibilityMeta),
+        _postMeta,
+        post.isAcceptableOrUnknown(data['post']!, _postMeta),
       );
+    } else if (isInserting) {
+      context.missing(_postMeta);
     }
     if (data.containsKey('last_modified')) {
       context.handle(
@@ -733,38 +648,18 @@ class $ComposeDraftsTable extends ComposeDrafts
   @override
   Set<GeneratedColumn> get $primaryKey => {id};
   @override
-  ComposeDraft map(Map<String, dynamic> data, {String? tablePrefix}) {
+  PostDraft map(Map<String, dynamic> data, {String? tablePrefix}) {
     final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
-    return ComposeDraft(
+    return PostDraft(
       id:
           attachedDatabase.typeMapping.read(
             DriftSqlType.string,
             data['${effectivePrefix}id'],
           )!,
-      title:
+      post:
           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'],
-          )!,
-      attachmentIds:
-          attachedDatabase.typeMapping.read(
-            DriftSqlType.string,
-            data['${effectivePrefix}attachment_ids'],
-          )!,
-      visibility:
-          attachedDatabase.typeMapping.read(
-            DriftSqlType.int,
-            data['${effectivePrefix}visibility'],
+            data['${effectivePrefix}post'],
           )!,
       lastModified:
           attachedDatabase.typeMapping.read(
@@ -775,65 +670,45 @@ class $ComposeDraftsTable extends ComposeDrafts
   }
 
   @override
-  $ComposeDraftsTable createAlias(String alias) {
-    return $ComposeDraftsTable(attachedDatabase, alias);
+  $PostDraftsTable createAlias(String alias) {
+    return $PostDraftsTable(attachedDatabase, alias);
   }
 }
 
-class ComposeDraft extends DataClass implements Insertable<ComposeDraft> {
+class PostDraft extends DataClass implements Insertable<PostDraft> {
   final String id;
-  final String title;
-  final String description;
-  final String content;
-  final String attachmentIds;
-  final int visibility;
+  final String post;
   final DateTime lastModified;
-  const ComposeDraft({
+  const PostDraft({
     required this.id,
-    required this.title,
-    required this.description,
-    required this.content,
-    required this.attachmentIds,
-    required this.visibility,
+    required this.post,
     required this.lastModified,
   });
   @override
   Map<String, Expression> toColumns(bool nullToAbsent) {
     final map = <String, Expression>{};
     map['id'] = Variable<String>(id);
-    map['title'] = Variable<String>(title);
-    map['description'] = Variable<String>(description);
-    map['content'] = Variable<String>(content);
-    map['attachment_ids'] = Variable<String>(attachmentIds);
-    map['visibility'] = Variable<int>(visibility);
+    map['post'] = Variable<String>(post);
     map['last_modified'] = Variable<DateTime>(lastModified);
     return map;
   }
 
-  ComposeDraftsCompanion toCompanion(bool nullToAbsent) {
-    return ComposeDraftsCompanion(
+  PostDraftsCompanion toCompanion(bool nullToAbsent) {
+    return PostDraftsCompanion(
       id: Value(id),
-      title: Value(title),
-      description: Value(description),
-      content: Value(content),
-      attachmentIds: Value(attachmentIds),
-      visibility: Value(visibility),
+      post: Value(post),
       lastModified: Value(lastModified),
     );
   }
 
-  factory ComposeDraft.fromJson(
+  factory PostDraft.fromJson(
     Map<String, dynamic> json, {
     ValueSerializer? serializer,
   }) {
     serializer ??= driftRuntimeOptions.defaultSerializer;
-    return ComposeDraft(
+    return PostDraft(
       id: serializer.fromJson<String>(json['id']),
-      title: serializer.fromJson<String>(json['title']),
-      description: serializer.fromJson<String>(json['description']),
-      content: serializer.fromJson<String>(json['content']),
-      attachmentIds: serializer.fromJson<String>(json['attachmentIds']),
-      visibility: serializer.fromJson<int>(json['visibility']),
+      post: serializer.fromJson<String>(json['post']),
       lastModified: serializer.fromJson<DateTime>(json['lastModified']),
     );
   }
@@ -842,45 +717,21 @@ class ComposeDraft extends DataClass implements Insertable<ComposeDraft> {
     serializer ??= driftRuntimeOptions.defaultSerializer;
     return <String, dynamic>{
       'id': serializer.toJson<String>(id),
-      'title': serializer.toJson<String>(title),
-      'description': serializer.toJson<String>(description),
-      'content': serializer.toJson<String>(content),
-      'attachmentIds': serializer.toJson<String>(attachmentIds),
-      'visibility': serializer.toJson<int>(visibility),
+      'post': serializer.toJson<String>(post),
       'lastModified': serializer.toJson<DateTime>(lastModified),
     };
   }
 
-  ComposeDraft copyWith({
-    String? id,
-    String? title,
-    String? description,
-    String? content,
-    String? attachmentIds,
-    int? visibility,
-    DateTime? lastModified,
-  }) => ComposeDraft(
-    id: id ?? this.id,
-    title: title ?? this.title,
-    description: description ?? this.description,
-    content: content ?? this.content,
-    attachmentIds: attachmentIds ?? this.attachmentIds,
-    visibility: visibility ?? this.visibility,
-    lastModified: lastModified ?? this.lastModified,
-  );
-  ComposeDraft copyWithCompanion(ComposeDraftsCompanion data) {
-    return ComposeDraft(
+  PostDraft copyWith({String? id, String? post, DateTime? lastModified}) =>
+      PostDraft(
+        id: id ?? this.id,
+        post: post ?? this.post,
+        lastModified: lastModified ?? this.lastModified,
+      );
+  PostDraft copyWithCompanion(PostDraftsCompanion data) {
+    return PostDraft(
       id: data.id.present ? data.id.value : this.id,
-      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,
-      attachmentIds:
-          data.attachmentIds.present
-              ? data.attachmentIds.value
-              : this.attachmentIds,
-      visibility:
-          data.visibility.present ? data.visibility.value : this.visibility,
+      post: data.post.present ? data.post.value : this.post,
       lastModified:
           data.lastModified.present
               ? data.lastModified.value
@@ -890,110 +741,67 @@ class ComposeDraft extends DataClass implements Insertable<ComposeDraft> {
 
   @override
   String toString() {
-    return (StringBuffer('ComposeDraft(')
+    return (StringBuffer('PostDraft(')
           ..write('id: $id, ')
-          ..write('title: $title, ')
-          ..write('description: $description, ')
-          ..write('content: $content, ')
-          ..write('attachmentIds: $attachmentIds, ')
-          ..write('visibility: $visibility, ')
+          ..write('post: $post, ')
           ..write('lastModified: $lastModified')
           ..write(')'))
         .toString();
   }
 
   @override
-  int get hashCode => Object.hash(
-    id,
-    title,
-    description,
-    content,
-    attachmentIds,
-    visibility,
-    lastModified,
-  );
+  int get hashCode => Object.hash(id, post, lastModified);
   @override
   bool operator ==(Object other) =>
       identical(this, other) ||
-      (other is ComposeDraft &&
+      (other is PostDraft &&
           other.id == this.id &&
-          other.title == this.title &&
-          other.description == this.description &&
-          other.content == this.content &&
-          other.attachmentIds == this.attachmentIds &&
-          other.visibility == this.visibility &&
+          other.post == this.post &&
           other.lastModified == this.lastModified);
 }
 
-class ComposeDraftsCompanion extends UpdateCompanion<ComposeDraft> {
+class PostDraftsCompanion extends UpdateCompanion<PostDraft> {
   final Value<String> id;
-  final Value<String> title;
-  final Value<String> description;
-  final Value<String> content;
-  final Value<String> attachmentIds;
-  final Value<int> visibility;
+  final Value<String> post;
   final Value<DateTime> lastModified;
   final Value<int> rowid;
-  const ComposeDraftsCompanion({
+  const PostDraftsCompanion({
     this.id = const Value.absent(),
-    this.title = const Value.absent(),
-    this.description = const Value.absent(),
-    this.content = const Value.absent(),
-    this.attachmentIds = const Value.absent(),
-    this.visibility = const Value.absent(),
+    this.post = const Value.absent(),
     this.lastModified = const Value.absent(),
     this.rowid = const Value.absent(),
   });
-  ComposeDraftsCompanion.insert({
+  PostDraftsCompanion.insert({
     required String id,
-    this.title = const Value.absent(),
-    this.description = const Value.absent(),
-    this.content = const Value.absent(),
-    this.attachmentIds = const Value.absent(),
-    this.visibility = const Value.absent(),
+    required String post,
     required DateTime lastModified,
     this.rowid = const Value.absent(),
   }) : id = Value(id),
+       post = Value(post),
        lastModified = Value(lastModified);
-  static Insertable<ComposeDraft> custom({
+  static Insertable<PostDraft> custom({
     Expression<String>? id,
-    Expression<String>? title,
-    Expression<String>? description,
-    Expression<String>? content,
-    Expression<String>? attachmentIds,
-    Expression<int>? visibility,
+    Expression<String>? post,
     Expression<DateTime>? lastModified,
     Expression<int>? rowid,
   }) {
     return RawValuesInsertable({
       if (id != null) 'id': id,
-      if (title != null) 'title': title,
-      if (description != null) 'description': description,
-      if (content != null) 'content': content,
-      if (attachmentIds != null) 'attachment_ids': attachmentIds,
-      if (visibility != null) 'visibility': visibility,
+      if (post != null) 'post': post,
       if (lastModified != null) 'last_modified': lastModified,
       if (rowid != null) 'rowid': rowid,
     });
   }
 
-  ComposeDraftsCompanion copyWith({
+  PostDraftsCompanion copyWith({
     Value<String>? id,
-    Value<String>? title,
-    Value<String>? description,
-    Value<String>? content,
-    Value<String>? attachmentIds,
-    Value<int>? visibility,
+    Value<String>? post,
     Value<DateTime>? lastModified,
     Value<int>? rowid,
   }) {
-    return ComposeDraftsCompanion(
+    return PostDraftsCompanion(
       id: id ?? this.id,
-      title: title ?? this.title,
-      description: description ?? this.description,
-      content: content ?? this.content,
-      attachmentIds: attachmentIds ?? this.attachmentIds,
-      visibility: visibility ?? this.visibility,
+      post: post ?? this.post,
       lastModified: lastModified ?? this.lastModified,
       rowid: rowid ?? this.rowid,
     );
@@ -1005,20 +813,8 @@ class ComposeDraftsCompanion extends UpdateCompanion<ComposeDraft> {
     if (id.present) {
       map['id'] = Variable<String>(id.value);
     }
-    if (title.present) {
-      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 (attachmentIds.present) {
-      map['attachment_ids'] = Variable<String>(attachmentIds.value);
-    }
-    if (visibility.present) {
-      map['visibility'] = Variable<int>(visibility.value);
+    if (post.present) {
+      map['post'] = Variable<String>(post.value);
     }
     if (lastModified.present) {
       map['last_modified'] = Variable<DateTime>(lastModified.value);
@@ -1031,430 +827,9 @@ class ComposeDraftsCompanion extends UpdateCompanion<ComposeDraft> {
 
   @override
   String toString() {
-    return (StringBuffer('ComposeDraftsCompanion(')
+    return (StringBuffer('PostDraftsCompanion(')
           ..write('id: $id, ')
-          ..write('title: $title, ')
-          ..write('description: $description, ')
-          ..write('content: $content, ')
-          ..write('attachmentIds: $attachmentIds, ')
-          ..write('visibility: $visibility, ')
-          ..write('lastModified: $lastModified, ')
-          ..write('rowid: $rowid')
-          ..write(')'))
-        .toString();
-  }
-}
-
-class $ArticleDraftsTable extends ArticleDrafts
-    with TableInfo<$ArticleDraftsTable, ArticleDraft> {
-  @override
-  final GeneratedDatabase attachedDatabase;
-  final String? _alias;
-  $ArticleDraftsTable(this.attachedDatabase, [this._alias]);
-  static const VerificationMeta _idMeta = const VerificationMeta('id');
-  @override
-  late final GeneratedColumn<String> id = GeneratedColumn<String>(
-    'id',
-    aliasedName,
-    false,
-    type: DriftSqlType.string,
-    requiredDuringInsert: true,
-  );
-  static const VerificationMeta _titleMeta = const VerificationMeta('title');
-  @override
-  late final GeneratedColumn<String> title = GeneratedColumn<String>(
-    'title',
-    aliasedName,
-    false,
-    type: DriftSqlType.string,
-    requiredDuringInsert: false,
-    defaultValue: const Constant(''),
-  );
-  static const VerificationMeta _descriptionMeta = const VerificationMeta(
-    'description',
-  );
-  @override
-  late final GeneratedColumn<String> description = GeneratedColumn<String>(
-    'description',
-    aliasedName,
-    false,
-    type: DriftSqlType.string,
-    requiredDuringInsert: false,
-    defaultValue: const Constant(''),
-  );
-  static const VerificationMeta _contentMeta = const VerificationMeta(
-    'content',
-  );
-  @override
-  late final GeneratedColumn<String> content = GeneratedColumn<String>(
-    'content',
-    aliasedName,
-    false,
-    type: DriftSqlType.string,
-    requiredDuringInsert: false,
-    defaultValue: const Constant(''),
-  );
-  static const VerificationMeta _visibilityMeta = const VerificationMeta(
-    'visibility',
-  );
-  @override
-  late final GeneratedColumn<int> visibility = GeneratedColumn<int>(
-    'visibility',
-    aliasedName,
-    false,
-    type: DriftSqlType.int,
-    requiredDuringInsert: false,
-    defaultValue: const Constant(0),
-  );
-  static const VerificationMeta _lastModifiedMeta = const VerificationMeta(
-    'lastModified',
-  );
-  @override
-  late final GeneratedColumn<DateTime> lastModified = GeneratedColumn<DateTime>(
-    'last_modified',
-    aliasedName,
-    false,
-    type: DriftSqlType.dateTime,
-    requiredDuringInsert: true,
-  );
-  @override
-  List<GeneratedColumn> get $columns => [
-    id,
-    title,
-    description,
-    content,
-    visibility,
-    lastModified,
-  ];
-  @override
-  String get aliasedName => _alias ?? actualTableName;
-  @override
-  String get actualTableName => $name;
-  static const String $name = 'article_drafts';
-  @override
-  VerificationContext validateIntegrity(
-    Insertable<ArticleDraft> instance, {
-    bool isInserting = false,
-  }) {
-    final context = VerificationContext();
-    final data = instance.toColumns(true);
-    if (data.containsKey('id')) {
-      context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
-    } else if (isInserting) {
-      context.missing(_idMeta);
-    }
-    if (data.containsKey('title')) {
-      context.handle(
-        _titleMeta,
-        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('last_modified')) {
-      context.handle(
-        _lastModifiedMeta,
-        lastModified.isAcceptableOrUnknown(
-          data['last_modified']!,
-          _lastModifiedMeta,
-        ),
-      );
-    } else if (isInserting) {
-      context.missing(_lastModifiedMeta);
-    }
-    return context;
-  }
-
-  @override
-  Set<GeneratedColumn> get $primaryKey => {id};
-  @override
-  ArticleDraft map(Map<String, dynamic> data, {String? tablePrefix}) {
-    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
-    return ArticleDraft(
-      id:
-          attachedDatabase.typeMapping.read(
-            DriftSqlType.string,
-            data['${effectivePrefix}id'],
-          )!,
-      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(
-            DriftSqlType.int,
-            data['${effectivePrefix}visibility'],
-          )!,
-      lastModified:
-          attachedDatabase.typeMapping.read(
-            DriftSqlType.dateTime,
-            data['${effectivePrefix}last_modified'],
-          )!,
-    );
-  }
-
-  @override
-  $ArticleDraftsTable createAlias(String alias) {
-    return $ArticleDraftsTable(attachedDatabase, alias);
-  }
-}
-
-class ArticleDraft extends DataClass implements Insertable<ArticleDraft> {
-  final String id;
-  final String title;
-  final String description;
-  final String content;
-  final int visibility;
-  final DateTime lastModified;
-  const ArticleDraft({
-    required this.id,
-    required this.title,
-    required this.description,
-    required this.content,
-    required this.visibility,
-    required this.lastModified,
-  });
-  @override
-  Map<String, Expression> toColumns(bool nullToAbsent) {
-    final map = <String, Expression>{};
-    map['id'] = Variable<String>(id);
-    map['title'] = Variable<String>(title);
-    map['description'] = Variable<String>(description);
-    map['content'] = Variable<String>(content);
-    map['visibility'] = Variable<int>(visibility);
-    map['last_modified'] = Variable<DateTime>(lastModified);
-    return map;
-  }
-
-  ArticleDraftsCompanion toCompanion(bool nullToAbsent) {
-    return ArticleDraftsCompanion(
-      id: Value(id),
-      title: Value(title),
-      description: Value(description),
-      content: Value(content),
-      visibility: Value(visibility),
-      lastModified: Value(lastModified),
-    );
-  }
-
-  factory ArticleDraft.fromJson(
-    Map<String, dynamic> json, {
-    ValueSerializer? serializer,
-  }) {
-    serializer ??= driftRuntimeOptions.defaultSerializer;
-    return ArticleDraft(
-      id: serializer.fromJson<String>(json['id']),
-      title: serializer.fromJson<String>(json['title']),
-      description: serializer.fromJson<String>(json['description']),
-      content: serializer.fromJson<String>(json['content']),
-      visibility: serializer.fromJson<int>(json['visibility']),
-      lastModified: serializer.fromJson<DateTime>(json['lastModified']),
-    );
-  }
-  @override
-  Map<String, dynamic> toJson({ValueSerializer? serializer}) {
-    serializer ??= driftRuntimeOptions.defaultSerializer;
-    return <String, dynamic>{
-      'id': serializer.toJson<String>(id),
-      'title': serializer.toJson<String>(title),
-      'description': serializer.toJson<String>(description),
-      'content': serializer.toJson<String>(content),
-      'visibility': serializer.toJson<int>(visibility),
-      'lastModified': serializer.toJson<DateTime>(lastModified),
-    };
-  }
-
-  ArticleDraft copyWith({
-    String? id,
-    String? title,
-    String? description,
-    String? content,
-    int? visibility,
-    DateTime? lastModified,
-  }) => ArticleDraft(
-    id: id ?? this.id,
-    title: title ?? this.title,
-    description: description ?? this.description,
-    content: content ?? this.content,
-    visibility: visibility ?? this.visibility,
-    lastModified: lastModified ?? this.lastModified,
-  );
-  ArticleDraft copyWithCompanion(ArticleDraftsCompanion data) {
-    return ArticleDraft(
-      id: data.id.present ? data.id.value : this.id,
-      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,
-      lastModified:
-          data.lastModified.present
-              ? data.lastModified.value
-              : this.lastModified,
-    );
-  }
-
-  @override
-  String toString() {
-    return (StringBuffer('ArticleDraft(')
-          ..write('id: $id, ')
-          ..write('title: $title, ')
-          ..write('description: $description, ')
-          ..write('content: $content, ')
-          ..write('visibility: $visibility, ')
-          ..write('lastModified: $lastModified')
-          ..write(')'))
-        .toString();
-  }
-
-  @override
-  int get hashCode =>
-      Object.hash(id, title, description, content, visibility, lastModified);
-  @override
-  bool operator ==(Object other) =>
-      identical(this, other) ||
-      (other is ArticleDraft &&
-          other.id == this.id &&
-          other.title == this.title &&
-          other.description == this.description &&
-          other.content == this.content &&
-          other.visibility == this.visibility &&
-          other.lastModified == this.lastModified);
-}
-
-class ArticleDraftsCompanion extends UpdateCompanion<ArticleDraft> {
-  final Value<String> id;
-  final Value<String> title;
-  final Value<String> description;
-  final Value<String> content;
-  final Value<int> visibility;
-  final Value<DateTime> lastModified;
-  final Value<int> rowid;
-  const ArticleDraftsCompanion({
-    this.id = const Value.absent(),
-    this.title = const Value.absent(),
-    this.description = const Value.absent(),
-    this.content = const Value.absent(),
-    this.visibility = const Value.absent(),
-    this.lastModified = const Value.absent(),
-    this.rowid = const Value.absent(),
-  });
-  ArticleDraftsCompanion.insert({
-    required String id,
-    this.title = const Value.absent(),
-    this.description = const Value.absent(),
-    this.content = const Value.absent(),
-    this.visibility = const Value.absent(),
-    required DateTime lastModified,
-    this.rowid = const Value.absent(),
-  }) : id = Value(id),
-       lastModified = Value(lastModified);
-  static Insertable<ArticleDraft> custom({
-    Expression<String>? id,
-    Expression<String>? title,
-    Expression<String>? description,
-    Expression<String>? content,
-    Expression<int>? visibility,
-    Expression<DateTime>? lastModified,
-    Expression<int>? rowid,
-  }) {
-    return RawValuesInsertable({
-      if (id != null) 'id': id,
-      if (title != null) 'title': title,
-      if (description != null) 'description': description,
-      if (content != null) 'content': content,
-      if (visibility != null) 'visibility': visibility,
-      if (lastModified != null) 'last_modified': lastModified,
-      if (rowid != null) 'rowid': rowid,
-    });
-  }
-
-  ArticleDraftsCompanion copyWith({
-    Value<String>? id,
-    Value<String>? title,
-    Value<String>? description,
-    Value<String>? content,
-    Value<int>? visibility,
-    Value<DateTime>? lastModified,
-    Value<int>? rowid,
-  }) {
-    return ArticleDraftsCompanion(
-      id: id ?? this.id,
-      title: title ?? this.title,
-      description: description ?? this.description,
-      content: content ?? this.content,
-      visibility: visibility ?? this.visibility,
-      lastModified: lastModified ?? this.lastModified,
-      rowid: rowid ?? this.rowid,
-    );
-  }
-
-  @override
-  Map<String, Expression> toColumns(bool nullToAbsent) {
-    final map = <String, Expression>{};
-    if (id.present) {
-      map['id'] = Variable<String>(id.value);
-    }
-    if (title.present) {
-      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 (lastModified.present) {
-      map['last_modified'] = Variable<DateTime>(lastModified.value);
-    }
-    if (rowid.present) {
-      map['rowid'] = Variable<int>(rowid.value);
-    }
-    return map;
-  }
-
-  @override
-  String toString() {
-    return (StringBuffer('ArticleDraftsCompanion(')
-          ..write('id: $id, ')
-          ..write('title: $title, ')
-          ..write('description: $description, ')
-          ..write('content: $content, ')
-          ..write('visibility: $visibility, ')
+          ..write('post: $post, ')
           ..write('lastModified: $lastModified, ')
           ..write('rowid: $rowid')
           ..write(')'))
@@ -1466,16 +841,14 @@ abstract class _$AppDatabase extends GeneratedDatabase {
   _$AppDatabase(QueryExecutor e) : super(e);
   $AppDatabaseManager get managers => $AppDatabaseManager(this);
   late final $ChatMessagesTable chatMessages = $ChatMessagesTable(this);
-  late final $ComposeDraftsTable composeDrafts = $ComposeDraftsTable(this);
-  late final $ArticleDraftsTable articleDrafts = $ArticleDraftsTable(this);
+  late final $PostDraftsTable postDrafts = $PostDraftsTable(this);
   @override
   Iterable<TableInfo<Table, Object?>> get allTables =>
       allSchemaEntities.whereType<TableInfo<Table, Object?>>();
   @override
   List<DatabaseSchemaEntity> get allSchemaEntities => [
     chatMessages,
-    composeDrafts,
-    articleDrafts,
+    postDrafts,
   ];
 }
 
@@ -1764,32 +1137,24 @@ typedef $$ChatMessagesTableProcessedTableManager =
       ChatMessage,
       PrefetchHooks Function()
     >;
-typedef $$ComposeDraftsTableCreateCompanionBuilder =
-    ComposeDraftsCompanion Function({
+typedef $$PostDraftsTableCreateCompanionBuilder =
+    PostDraftsCompanion Function({
       required String id,
-      Value<String> title,
-      Value<String> description,
-      Value<String> content,
-      Value<String> attachmentIds,
-      Value<int> visibility,
+      required String post,
       required DateTime lastModified,
       Value<int> rowid,
     });
-typedef $$ComposeDraftsTableUpdateCompanionBuilder =
-    ComposeDraftsCompanion Function({
+typedef $$PostDraftsTableUpdateCompanionBuilder =
+    PostDraftsCompanion Function({
       Value<String> id,
-      Value<String> title,
-      Value<String> description,
-      Value<String> content,
-      Value<String> attachmentIds,
-      Value<int> visibility,
+      Value<String> post,
       Value<DateTime> lastModified,
       Value<int> rowid,
     });
 
-class $$ComposeDraftsTableFilterComposer
-    extends Composer<_$AppDatabase, $ComposeDraftsTable> {
-  $$ComposeDraftsTableFilterComposer({
+class $$PostDraftsTableFilterComposer
+    extends Composer<_$AppDatabase, $PostDraftsTable> {
+  $$PostDraftsTableFilterComposer({
     required super.$db,
     required super.$table,
     super.joinBuilder,
@@ -1801,28 +1166,8 @@ class $$ComposeDraftsTableFilterComposer
     builder: (column) => ColumnFilters(column),
   );
 
-  ColumnFilters<String> get title => $composableBuilder(
-    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<String> get attachmentIds => $composableBuilder(
-    column: $table.attachmentIds,
-    builder: (column) => ColumnFilters(column),
-  );
-
-  ColumnFilters<int> get visibility => $composableBuilder(
-    column: $table.visibility,
+  ColumnFilters<String> get post => $composableBuilder(
+    column: $table.post,
     builder: (column) => ColumnFilters(column),
   );
 
@@ -1832,9 +1177,9 @@ class $$ComposeDraftsTableFilterComposer
   );
 }
 
-class $$ComposeDraftsTableOrderingComposer
-    extends Composer<_$AppDatabase, $ComposeDraftsTable> {
-  $$ComposeDraftsTableOrderingComposer({
+class $$PostDraftsTableOrderingComposer
+    extends Composer<_$AppDatabase, $PostDraftsTable> {
+  $$PostDraftsTableOrderingComposer({
     required super.$db,
     required super.$table,
     super.joinBuilder,
@@ -1846,28 +1191,8 @@ class $$ComposeDraftsTableOrderingComposer
     builder: (column) => ColumnOrderings(column),
   );
 
-  ColumnOrderings<String> get title => $composableBuilder(
-    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<String> get attachmentIds => $composableBuilder(
-    column: $table.attachmentIds,
-    builder: (column) => ColumnOrderings(column),
-  );
-
-  ColumnOrderings<int> get visibility => $composableBuilder(
-    column: $table.visibility,
+  ColumnOrderings<String> get post => $composableBuilder(
+    column: $table.post,
     builder: (column) => ColumnOrderings(column),
   );
 
@@ -1877,9 +1202,9 @@ class $$ComposeDraftsTableOrderingComposer
   );
 }
 
-class $$ComposeDraftsTableAnnotationComposer
-    extends Composer<_$AppDatabase, $ComposeDraftsTable> {
-  $$ComposeDraftsTableAnnotationComposer({
+class $$PostDraftsTableAnnotationComposer
+    extends Composer<_$AppDatabase, $PostDraftsTable> {
+  $$PostDraftsTableAnnotationComposer({
     required super.$db,
     required super.$table,
     super.joinBuilder,
@@ -1889,26 +1214,8 @@ class $$ComposeDraftsTableAnnotationComposer
   GeneratedColumn<String> get id =>
       $composableBuilder(column: $table.id, builder: (column) => column);
 
-  GeneratedColumn<String> get title =>
-      $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<String> get attachmentIds => $composableBuilder(
-    column: $table.attachmentIds,
-    builder: (column) => column,
-  );
-
-  GeneratedColumn<int> get visibility => $composableBuilder(
-    column: $table.visibility,
-    builder: (column) => column,
-  );
+  GeneratedColumn<String> get post =>
+      $composableBuilder(column: $table.post, builder: (column) => column);
 
   GeneratedColumn<DateTime> get lastModified => $composableBuilder(
     column: $table.lastModified,
@@ -1916,76 +1223,56 @@ class $$ComposeDraftsTableAnnotationComposer
   );
 }
 
-class $$ComposeDraftsTableTableManager
+class $$PostDraftsTableTableManager
     extends
         RootTableManager<
           _$AppDatabase,
-          $ComposeDraftsTable,
-          ComposeDraft,
-          $$ComposeDraftsTableFilterComposer,
-          $$ComposeDraftsTableOrderingComposer,
-          $$ComposeDraftsTableAnnotationComposer,
-          $$ComposeDraftsTableCreateCompanionBuilder,
-          $$ComposeDraftsTableUpdateCompanionBuilder,
+          $PostDraftsTable,
+          PostDraft,
+          $$PostDraftsTableFilterComposer,
+          $$PostDraftsTableOrderingComposer,
+          $$PostDraftsTableAnnotationComposer,
+          $$PostDraftsTableCreateCompanionBuilder,
+          $$PostDraftsTableUpdateCompanionBuilder,
           (
-            ComposeDraft,
-            BaseReferences<_$AppDatabase, $ComposeDraftsTable, ComposeDraft>,
+            PostDraft,
+            BaseReferences<_$AppDatabase, $PostDraftsTable, PostDraft>,
           ),
-          ComposeDraft,
+          PostDraft,
           PrefetchHooks Function()
         > {
-  $$ComposeDraftsTableTableManager(_$AppDatabase db, $ComposeDraftsTable table)
+  $$PostDraftsTableTableManager(_$AppDatabase db, $PostDraftsTable table)
     : super(
         TableManagerState(
           db: db,
           table: table,
           createFilteringComposer:
-              () => $$ComposeDraftsTableFilterComposer($db: db, $table: table),
+              () => $$PostDraftsTableFilterComposer($db: db, $table: table),
           createOrderingComposer:
-              () =>
-                  $$ComposeDraftsTableOrderingComposer($db: db, $table: table),
+              () => $$PostDraftsTableOrderingComposer($db: db, $table: table),
           createComputedFieldComposer:
-              () => $$ComposeDraftsTableAnnotationComposer(
-                $db: db,
-                $table: table,
-              ),
+              () => $$PostDraftsTableAnnotationComposer($db: db, $table: table),
           updateCompanionCallback:
               ({
                 Value<String> id = const Value.absent(),
-                Value<String> title = const Value.absent(),
-                Value<String> description = const Value.absent(),
-                Value<String> content = const Value.absent(),
-                Value<String> attachmentIds = const Value.absent(),
-                Value<int> visibility = const Value.absent(),
+                Value<String> post = const Value.absent(),
                 Value<DateTime> lastModified = const Value.absent(),
                 Value<int> rowid = const Value.absent(),
-              }) => ComposeDraftsCompanion(
+              }) => PostDraftsCompanion(
                 id: id,
-                title: title,
-                description: description,
-                content: content,
-                attachmentIds: attachmentIds,
-                visibility: visibility,
+                post: post,
                 lastModified: lastModified,
                 rowid: rowid,
               ),
           createCompanionCallback:
               ({
                 required String id,
-                Value<String> title = const Value.absent(),
-                Value<String> description = const Value.absent(),
-                Value<String> content = const Value.absent(),
-                Value<String> attachmentIds = const Value.absent(),
-                Value<int> visibility = const Value.absent(),
+                required String post,
                 required DateTime lastModified,
                 Value<int> rowid = const Value.absent(),
-              }) => ComposeDraftsCompanion.insert(
+              }) => PostDraftsCompanion.insert(
                 id: id,
-                title: title,
-                description: description,
-                content: content,
-                attachmentIds: attachmentIds,
-                visibility: visibility,
+                post: post,
                 lastModified: lastModified,
                 rowid: rowid,
               ),
@@ -2004,257 +1291,18 @@ class $$ComposeDraftsTableTableManager
       );
 }
 
-typedef $$ComposeDraftsTableProcessedTableManager =
+typedef $$PostDraftsTableProcessedTableManager =
     ProcessedTableManager<
       _$AppDatabase,
-      $ComposeDraftsTable,
-      ComposeDraft,
-      $$ComposeDraftsTableFilterComposer,
-      $$ComposeDraftsTableOrderingComposer,
-      $$ComposeDraftsTableAnnotationComposer,
-      $$ComposeDraftsTableCreateCompanionBuilder,
-      $$ComposeDraftsTableUpdateCompanionBuilder,
-      (
-        ComposeDraft,
-        BaseReferences<_$AppDatabase, $ComposeDraftsTable, ComposeDraft>,
-      ),
-      ComposeDraft,
-      PrefetchHooks Function()
-    >;
-typedef $$ArticleDraftsTableCreateCompanionBuilder =
-    ArticleDraftsCompanion Function({
-      required String id,
-      Value<String> title,
-      Value<String> description,
-      Value<String> content,
-      Value<int> visibility,
-      required DateTime lastModified,
-      Value<int> rowid,
-    });
-typedef $$ArticleDraftsTableUpdateCompanionBuilder =
-    ArticleDraftsCompanion Function({
-      Value<String> id,
-      Value<String> title,
-      Value<String> description,
-      Value<String> content,
-      Value<int> visibility,
-      Value<DateTime> lastModified,
-      Value<int> rowid,
-    });
-
-class $$ArticleDraftsTableFilterComposer
-    extends Composer<_$AppDatabase, $ArticleDraftsTable> {
-  $$ArticleDraftsTableFilterComposer({
-    required super.$db,
-    required super.$table,
-    super.joinBuilder,
-    super.$addJoinBuilderToRootComposer,
-    super.$removeJoinBuilderFromRootComposer,
-  });
-  ColumnFilters<String> get id => $composableBuilder(
-    column: $table.id,
-    builder: (column) => ColumnFilters(column),
-  );
-
-  ColumnFilters<String> get title => $composableBuilder(
-    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<DateTime> get lastModified => $composableBuilder(
-    column: $table.lastModified,
-    builder: (column) => ColumnFilters(column),
-  );
-}
-
-class $$ArticleDraftsTableOrderingComposer
-    extends Composer<_$AppDatabase, $ArticleDraftsTable> {
-  $$ArticleDraftsTableOrderingComposer({
-    required super.$db,
-    required super.$table,
-    super.joinBuilder,
-    super.$addJoinBuilderToRootComposer,
-    super.$removeJoinBuilderFromRootComposer,
-  });
-  ColumnOrderings<String> get id => $composableBuilder(
-    column: $table.id,
-    builder: (column) => ColumnOrderings(column),
-  );
-
-  ColumnOrderings<String> get title => $composableBuilder(
-    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<DateTime> get lastModified => $composableBuilder(
-    column: $table.lastModified,
-    builder: (column) => ColumnOrderings(column),
-  );
-}
-
-class $$ArticleDraftsTableAnnotationComposer
-    extends Composer<_$AppDatabase, $ArticleDraftsTable> {
-  $$ArticleDraftsTableAnnotationComposer({
-    required super.$db,
-    required super.$table,
-    super.joinBuilder,
-    super.$addJoinBuilderToRootComposer,
-    super.$removeJoinBuilderFromRootComposer,
-  });
-  GeneratedColumn<String> get id =>
-      $composableBuilder(column: $table.id, builder: (column) => column);
-
-  GeneratedColumn<String> get title =>
-      $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<DateTime> get lastModified => $composableBuilder(
-    column: $table.lastModified,
-    builder: (column) => column,
-  );
-}
-
-class $$ArticleDraftsTableTableManager
-    extends
-        RootTableManager<
-          _$AppDatabase,
-          $ArticleDraftsTable,
-          ArticleDraft,
-          $$ArticleDraftsTableFilterComposer,
-          $$ArticleDraftsTableOrderingComposer,
-          $$ArticleDraftsTableAnnotationComposer,
-          $$ArticleDraftsTableCreateCompanionBuilder,
-          $$ArticleDraftsTableUpdateCompanionBuilder,
-          (
-            ArticleDraft,
-            BaseReferences<_$AppDatabase, $ArticleDraftsTable, ArticleDraft>,
-          ),
-          ArticleDraft,
-          PrefetchHooks Function()
-        > {
-  $$ArticleDraftsTableTableManager(_$AppDatabase db, $ArticleDraftsTable table)
-    : super(
-        TableManagerState(
-          db: db,
-          table: table,
-          createFilteringComposer:
-              () => $$ArticleDraftsTableFilterComposer($db: db, $table: table),
-          createOrderingComposer:
-              () =>
-                  $$ArticleDraftsTableOrderingComposer($db: db, $table: table),
-          createComputedFieldComposer:
-              () => $$ArticleDraftsTableAnnotationComposer(
-                $db: db,
-                $table: table,
-              ),
-          updateCompanionCallback:
-              ({
-                Value<String> id = 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<DateTime> lastModified = const Value.absent(),
-                Value<int> rowid = const Value.absent(),
-              }) => ArticleDraftsCompanion(
-                id: id,
-                title: title,
-                description: description,
-                content: content,
-                visibility: visibility,
-                lastModified: lastModified,
-                rowid: rowid,
-              ),
-          createCompanionCallback:
-              ({
-                required String id,
-                Value<String> title = const Value.absent(),
-                Value<String> description = const Value.absent(),
-                Value<String> content = const Value.absent(),
-                Value<int> visibility = const Value.absent(),
-                required DateTime lastModified,
-                Value<int> rowid = const Value.absent(),
-              }) => ArticleDraftsCompanion.insert(
-                id: id,
-                title: title,
-                description: description,
-                content: content,
-                visibility: visibility,
-                lastModified: lastModified,
-                rowid: rowid,
-              ),
-          withReferenceMapper:
-              (p0) =>
-                  p0
-                      .map(
-                        (e) => (
-                          e.readTable(table),
-                          BaseReferences(db, table, e),
-                        ),
-                      )
-                      .toList(),
-          prefetchHooksCallback: null,
-        ),
-      );
-}
-
-typedef $$ArticleDraftsTableProcessedTableManager =
-    ProcessedTableManager<
-      _$AppDatabase,
-      $ArticleDraftsTable,
-      ArticleDraft,
-      $$ArticleDraftsTableFilterComposer,
-      $$ArticleDraftsTableOrderingComposer,
-      $$ArticleDraftsTableAnnotationComposer,
-      $$ArticleDraftsTableCreateCompanionBuilder,
-      $$ArticleDraftsTableUpdateCompanionBuilder,
-      (
-        ArticleDraft,
-        BaseReferences<_$AppDatabase, $ArticleDraftsTable, ArticleDraft>,
-      ),
-      ArticleDraft,
+      $PostDraftsTable,
+      PostDraft,
+      $$PostDraftsTableFilterComposer,
+      $$PostDraftsTableOrderingComposer,
+      $$PostDraftsTableAnnotationComposer,
+      $$PostDraftsTableCreateCompanionBuilder,
+      $$PostDraftsTableUpdateCompanionBuilder,
+      (PostDraft, BaseReferences<_$AppDatabase, $PostDraftsTable, PostDraft>),
+      PostDraft,
       PrefetchHooks Function()
     >;
 
@@ -2263,8 +1311,6 @@ class $AppDatabaseManager {
   $AppDatabaseManager(this._db);
   $$ChatMessagesTableTableManager get chatMessages =>
       $$ChatMessagesTableTableManager(_db, _db.chatMessages);
-  $$ComposeDraftsTableTableManager get composeDrafts =>
-      $$ComposeDraftsTableTableManager(_db, _db.composeDrafts);
-  $$ArticleDraftsTableTableManager get articleDrafts =>
-      $$ArticleDraftsTableTableManager(_db, _db.articleDrafts);
+  $$PostDraftsTableTableManager get postDrafts =>
+      $$PostDraftsTableTableManager(_db, _db.postDrafts);
 }
diff --git a/lib/models/post.dart b/lib/models/post.dart
index d1ae308..350c724 100644
--- a/lib/models/post.dart
+++ b/lib/models/post.dart
@@ -9,36 +9,36 @@ part 'post.g.dart';
 sealed class SnPost with _$SnPost {
   const factory SnPost({
     required String id,
-    required String? title,
-    required String? description,
-    required String? language,
-    required DateTime? editedAt,
-    required DateTime publishedAt,
-    required int visibility,
-    required String? content,
-    required int type,
-    required Map<String, dynamic>? meta,
-    required int viewsUnique,
-    required int viewsTotal,
-    required int upvotes,
-    required int downvotes,
-    required int repliesCount,
-    required String? threadedPostId,
-    required SnPost? threadedPost,
-    required String? repliedPostId,
-    required SnPost? repliedPost,
-    required String? forwardedPostId,
-    required SnPost? forwardedPost,
-    required List<SnCloudFile> attachments,
-    required SnPublisher publisher,
+    String? title,
+    String? description,
+    String? language,
+    DateTime? editedAt,
+    @Default(null) DateTime? publishedAt,
+    @Default(0) int visibility,
+    String? content,
+    @Default(0) int type,
+    Map<String, dynamic>? meta,
+    @Default(0) int viewsUnique,
+    @Default(0) int viewsTotal,
+    @Default(0) int upvotes,
+    @Default(0) int downvotes,
+    @Default(0) int repliesCount,
+    String? threadedPostId,
+    SnPost? threadedPost,
+    String? repliedPostId,
+    SnPost? repliedPost,
+    String? forwardedPostId,
+    SnPost? forwardedPost,
+    @Default([]) List<SnCloudFile> attachments,
+    @Default(SnPublisher()) SnPublisher publisher,
     @Default({}) Map<String, int> reactionsCount,
-    required List<dynamic> reactions,
-    required List<dynamic> tags,
-    required List<dynamic> categories,
-    required List<dynamic> collections,
-    required DateTime createdAt,
-    required DateTime updatedAt,
-    required DateTime? deletedAt,
+    @Default([]) List<dynamic> reactions,
+    @Default([]) List<dynamic> tags,
+    @Default([]) List<dynamic> categories,
+    @Default([]) List<dynamic> collections,
+    @Default(null) DateTime? createdAt,
+    @Default(null) DateTime? updatedAt,
+    DateTime? deletedAt,
     @Default(false) bool isTruncated,
   }) = _SnPost;
 
@@ -48,20 +48,20 @@ sealed class SnPost with _$SnPost {
 @freezed
 sealed class SnPublisher with _$SnPublisher {
   const factory SnPublisher({
-    required String id,
-    required int type,
-    required String name,
-    required String nick,
+    @Default('') String id,
+    @Default(0) int type,
+    @Default('') String name,
+    @Default('') String nick,
     @Default('') String bio,
-    required SnCloudFile? picture,
-    required SnCloudFile? background,
-    required SnAccount? account,
-    required String? accountId,
-    required DateTime createdAt,
-    required DateTime updatedAt,
-    required DateTime? deletedAt,
-    required String? realmId,
-    required SnVerificationMark? verification,
+    SnCloudFile? picture,
+    SnCloudFile? background,
+    SnAccount? account,
+    String? accountId,
+    @Default(null) DateTime? createdAt,
+    @Default(null) DateTime? updatedAt,
+    DateTime? deletedAt,
+    String? realmId,
+    SnVerificationMark? verification,
   }) = _SnPublisher;
 
   factory SnPublisher.fromJson(Map<String, dynamic> json) =>
diff --git a/lib/models/post.freezed.dart b/lib/models/post.freezed.dart
index ed78355..193127b 100644
--- a/lib/models/post.freezed.dart
+++ b/lib/models/post.freezed.dart
@@ -16,7 +16,7 @@ T _$identity<T>(T value) => value;
 /// @nodoc
 mixin _$SnPost {
 
- String get id; String? get title; String? get description; String? get language; DateTime? get editedAt; DateTime get publishedAt; int get visibility; String? get content; int get type; Map<String, dynamic>? get meta; int get viewsUnique; int get viewsTotal; int get upvotes; int get downvotes; int get repliesCount; String? get threadedPostId; SnPost? get threadedPost; String? get repliedPostId; SnPost? get repliedPost; String? get forwardedPostId; SnPost? get forwardedPost; List<SnCloudFile> get attachments; SnPublisher get publisher; Map<String, int> get reactionsCount; List<dynamic> get reactions; List<dynamic> get tags; List<dynamic> get categories; List<dynamic> get collections; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; bool get isTruncated;
+ String get id; String? get title; String? get description; String? get language; DateTime? get editedAt; DateTime? get publishedAt; int get visibility; String? get content; int get type; Map<String, dynamic>? get meta; int get viewsUnique; int get viewsTotal; int get upvotes; int get downvotes; int get repliesCount; String? get threadedPostId; SnPost? get threadedPost; String? get repliedPostId; SnPost? get repliedPost; String? get forwardedPostId; SnPost? get forwardedPost; List<SnCloudFile> get attachments; SnPublisher get publisher; Map<String, int> get reactionsCount; List<dynamic> get reactions; List<dynamic> get tags; List<dynamic> get categories; List<dynamic> get collections; DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; bool get isTruncated;
 /// Create a copy of SnPost
 /// with the given fields replaced by the non-null parameter values.
 @JsonKey(includeFromJson: false, includeToJson: false)
@@ -49,7 +49,7 @@ abstract mixin class $SnPostCopyWith<$Res>  {
   factory $SnPostCopyWith(SnPost value, $Res Function(SnPost) _then) = _$SnPostCopyWithImpl;
 @useResult
 $Res call({
- String id, String? title, String? description, String? language, DateTime? editedAt, DateTime publishedAt, int visibility, String? content, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, List<dynamic> reactions, List<dynamic> tags, List<dynamic> categories, List<dynamic> collections, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, bool isTruncated
+ String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, List<dynamic> reactions, List<dynamic> tags, List<dynamic> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated
 });
 
 
@@ -66,15 +66,15 @@ class _$SnPostCopyWithImpl<$Res>
 
 /// Create a copy of SnPost
 /// with the given fields replaced by the non-null parameter values.
-@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = null,Object? visibility = null,Object? content = freezed,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? isTruncated = null,}) {
+@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? isTruncated = null,}) {
   return _then(_self.copyWith(
 id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
 as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
 as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
 as String?,language: freezed == language ? _self.language : language // ignore: cast_nullable_to_non_nullable
 as String?,editedAt: freezed == editedAt ? _self.editedAt : editedAt // ignore: cast_nullable_to_non_nullable
-as DateTime?,publishedAt: null == publishedAt ? _self.publishedAt : publishedAt // ignore: cast_nullable_to_non_nullable
-as DateTime,visibility: null == visibility ? _self.visibility : visibility // ignore: cast_nullable_to_non_nullable
+as DateTime?,publishedAt: freezed == publishedAt ? _self.publishedAt : publishedAt // ignore: cast_nullable_to_non_nullable
+as DateTime?,visibility: null == visibility ? _self.visibility : visibility // ignore: cast_nullable_to_non_nullable
 as int,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable
 as String?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
 as int,meta: freezed == meta ? _self.meta : meta // ignore: cast_nullable_to_non_nullable
@@ -96,9 +96,9 @@ as Map<String, int>,reactions: null == reactions ? _self.reactions : reactions /
 as List<dynamic>,tags: null == tags ? _self.tags : tags // ignore: cast_nullable_to_non_nullable
 as List<dynamic>,categories: null == categories ? _self.categories : categories // ignore: cast_nullable_to_non_nullable
 as List<dynamic>,collections: null == collections ? _self.collections : collections // ignore: cast_nullable_to_non_nullable
-as List<dynamic>,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
-as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
-as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
+as List<dynamic>,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
+as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
+as DateTime?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
 as DateTime?,isTruncated: null == isTruncated ? _self.isTruncated : isTruncated // ignore: cast_nullable_to_non_nullable
 as bool,
   ));
@@ -156,7 +156,7 @@ $SnPublisherCopyWith<$Res> get publisher {
 @JsonSerializable()
 
 class _SnPost implements SnPost {
-  const _SnPost({required this.id, required this.title, required this.description, required this.language, required this.editedAt, required this.publishedAt, required this.visibility, required this.content, required this.type, required final  Map<String, dynamic>? meta, required this.viewsUnique, required this.viewsTotal, required this.upvotes, required this.downvotes, required this.repliesCount, required this.threadedPostId, required this.threadedPost, required this.repliedPostId, required this.repliedPost, required this.forwardedPostId, required this.forwardedPost, required final  List<SnCloudFile> attachments, required this.publisher, final  Map<String, int> reactionsCount = const {}, required final  List<dynamic> reactions, required final  List<dynamic> tags, required final  List<dynamic> categories, required final  List<dynamic> collections, required this.createdAt, required this.updatedAt, required this.deletedAt, this.isTruncated = false}): _meta = meta,_attachments = attachments,_reactionsCount = reactionsCount,_reactions = reactions,_tags = tags,_categories = categories,_collections = collections;
+  const _SnPost({required this.id, this.title, this.description, this.language, this.editedAt, this.publishedAt = null, this.visibility = 0, this.content, this.type = 0, final  Map<String, dynamic>? meta, this.viewsUnique = 0, this.viewsTotal = 0, this.upvotes = 0, this.downvotes = 0, this.repliesCount = 0, this.threadedPostId, this.threadedPost, this.repliedPostId, this.repliedPost, this.forwardedPostId, this.forwardedPost, final  List<SnCloudFile> attachments = const [], this.publisher = const SnPublisher(), final  Map<String, int> reactionsCount = const {}, final  List<dynamic> reactions = const [], final  List<dynamic> tags = const [], final  List<dynamic> categories = const [], final  List<dynamic> collections = const [], this.createdAt = null, this.updatedAt = null, this.deletedAt, this.isTruncated = false}): _meta = meta,_attachments = attachments,_reactionsCount = reactionsCount,_reactions = reactions,_tags = tags,_categories = categories,_collections = collections;
   factory _SnPost.fromJson(Map<String, dynamic> json) => _$SnPostFromJson(json);
 
 @override final  String id;
@@ -164,10 +164,10 @@ class _SnPost implements SnPost {
 @override final  String? description;
 @override final  String? language;
 @override final  DateTime? editedAt;
-@override final  DateTime publishedAt;
-@override final  int visibility;
+@override@JsonKey() final  DateTime? publishedAt;
+@override@JsonKey() final  int visibility;
 @override final  String? content;
-@override final  int type;
+@override@JsonKey() final  int type;
  final  Map<String, dynamic>? _meta;
 @override Map<String, dynamic>? get meta {
   final value = _meta;
@@ -177,11 +177,11 @@ class _SnPost implements SnPost {
   return EqualUnmodifiableMapView(value);
 }
 
-@override final  int viewsUnique;
-@override final  int viewsTotal;
-@override final  int upvotes;
-@override final  int downvotes;
-@override final  int repliesCount;
+@override@JsonKey() final  int viewsUnique;
+@override@JsonKey() final  int viewsTotal;
+@override@JsonKey() final  int upvotes;
+@override@JsonKey() final  int downvotes;
+@override@JsonKey() final  int repliesCount;
 @override final  String? threadedPostId;
 @override final  SnPost? threadedPost;
 @override final  String? repliedPostId;
@@ -189,13 +189,13 @@ class _SnPost implements SnPost {
 @override final  String? forwardedPostId;
 @override final  SnPost? forwardedPost;
  final  List<SnCloudFile> _attachments;
-@override List<SnCloudFile> get attachments {
+@override@JsonKey() List<SnCloudFile> get attachments {
   if (_attachments is EqualUnmodifiableListView) return _attachments;
   // ignore: implicit_dynamic_type
   return EqualUnmodifiableListView(_attachments);
 }
 
-@override final  SnPublisher publisher;
+@override@JsonKey() final  SnPublisher publisher;
  final  Map<String, int> _reactionsCount;
 @override@JsonKey() Map<String, int> get reactionsCount {
   if (_reactionsCount is EqualUnmodifiableMapView) return _reactionsCount;
@@ -204,35 +204,35 @@ class _SnPost implements SnPost {
 }
 
  final  List<dynamic> _reactions;
-@override List<dynamic> get reactions {
+@override@JsonKey() List<dynamic> get reactions {
   if (_reactions is EqualUnmodifiableListView) return _reactions;
   // ignore: implicit_dynamic_type
   return EqualUnmodifiableListView(_reactions);
 }
 
  final  List<dynamic> _tags;
-@override List<dynamic> get tags {
+@override@JsonKey() List<dynamic> get tags {
   if (_tags is EqualUnmodifiableListView) return _tags;
   // ignore: implicit_dynamic_type
   return EqualUnmodifiableListView(_tags);
 }
 
  final  List<dynamic> _categories;
-@override List<dynamic> get categories {
+@override@JsonKey() List<dynamic> get categories {
   if (_categories is EqualUnmodifiableListView) return _categories;
   // ignore: implicit_dynamic_type
   return EqualUnmodifiableListView(_categories);
 }
 
  final  List<dynamic> _collections;
-@override List<dynamic> get collections {
+@override@JsonKey() List<dynamic> get collections {
   if (_collections is EqualUnmodifiableListView) return _collections;
   // ignore: implicit_dynamic_type
   return EqualUnmodifiableListView(_collections);
 }
 
-@override final  DateTime createdAt;
-@override final  DateTime updatedAt;
+@override@JsonKey() final  DateTime? createdAt;
+@override@JsonKey() final  DateTime? updatedAt;
 @override final  DateTime? deletedAt;
 @override@JsonKey() final  bool isTruncated;
 
@@ -269,7 +269,7 @@ abstract mixin class _$SnPostCopyWith<$Res> implements $SnPostCopyWith<$Res> {
   factory _$SnPostCopyWith(_SnPost value, $Res Function(_SnPost) _then) = __$SnPostCopyWithImpl;
 @override @useResult
 $Res call({
- String id, String? title, String? description, String? language, DateTime? editedAt, DateTime publishedAt, int visibility, String? content, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, List<dynamic> reactions, List<dynamic> tags, List<dynamic> categories, List<dynamic> collections, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, bool isTruncated
+ String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, List<dynamic> reactions, List<dynamic> tags, List<dynamic> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated
 });
 
 
@@ -286,15 +286,15 @@ class __$SnPostCopyWithImpl<$Res>
 
 /// Create a copy of SnPost
 /// with the given fields replaced by the non-null parameter values.
-@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = null,Object? visibility = null,Object? content = freezed,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? isTruncated = null,}) {
+@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? isTruncated = null,}) {
   return _then(_SnPost(
 id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
 as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
 as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
 as String?,language: freezed == language ? _self.language : language // ignore: cast_nullable_to_non_nullable
 as String?,editedAt: freezed == editedAt ? _self.editedAt : editedAt // ignore: cast_nullable_to_non_nullable
-as DateTime?,publishedAt: null == publishedAt ? _self.publishedAt : publishedAt // ignore: cast_nullable_to_non_nullable
-as DateTime,visibility: null == visibility ? _self.visibility : visibility // ignore: cast_nullable_to_non_nullable
+as DateTime?,publishedAt: freezed == publishedAt ? _self.publishedAt : publishedAt // ignore: cast_nullable_to_non_nullable
+as DateTime?,visibility: null == visibility ? _self.visibility : visibility // ignore: cast_nullable_to_non_nullable
 as int,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable
 as String?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
 as int,meta: freezed == meta ? _self._meta : meta // ignore: cast_nullable_to_non_nullable
@@ -316,9 +316,9 @@ as Map<String, int>,reactions: null == reactions ? _self._reactions : reactions
 as List<dynamic>,tags: null == tags ? _self._tags : tags // ignore: cast_nullable_to_non_nullable
 as List<dynamic>,categories: null == categories ? _self._categories : categories // ignore: cast_nullable_to_non_nullable
 as List<dynamic>,collections: null == collections ? _self._collections : collections // ignore: cast_nullable_to_non_nullable
-as List<dynamic>,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
-as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
-as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
+as List<dynamic>,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
+as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
+as DateTime?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
 as DateTime?,isTruncated: null == isTruncated ? _self.isTruncated : isTruncated // ignore: cast_nullable_to_non_nullable
 as bool,
   ));
@@ -376,7 +376,7 @@ $SnPublisherCopyWith<$Res> get publisher {
 /// @nodoc
 mixin _$SnPublisher {
 
- String get id; int get type; String get name; String get nick; String get bio; SnCloudFile? get picture; SnCloudFile? get background; SnAccount? get account; String? get accountId; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String? get realmId; SnVerificationMark? get verification;
+ String get id; int get type; String get name; String get nick; String get bio; SnCloudFile? get picture; SnCloudFile? get background; SnAccount? get account; String? get accountId; DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; String? get realmId; SnVerificationMark? get verification;
 /// Create a copy of SnPublisher
 /// with the given fields replaced by the non-null parameter values.
 @JsonKey(includeFromJson: false, includeToJson: false)
@@ -409,7 +409,7 @@ abstract mixin class $SnPublisherCopyWith<$Res>  {
   factory $SnPublisherCopyWith(SnPublisher value, $Res Function(SnPublisher) _then) = _$SnPublisherCopyWithImpl;
 @useResult
 $Res call({
- String id, int type, String name, String nick, String bio, SnCloudFile? picture, SnCloudFile? background, SnAccount? account, String? accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String? realmId, SnVerificationMark? verification
+ String id, int type, String name, String nick, String bio, SnCloudFile? picture, SnCloudFile? background, SnAccount? account, String? accountId, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String? realmId, SnVerificationMark? verification
 });
 
 
@@ -426,7 +426,7 @@ class _$SnPublisherCopyWithImpl<$Res>
 
 /// Create a copy of SnPublisher
 /// with the given fields replaced by the non-null parameter values.
-@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? type = null,Object? name = null,Object? nick = null,Object? bio = null,Object? picture = freezed,Object? background = freezed,Object? account = freezed,Object? accountId = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? realmId = freezed,Object? verification = freezed,}) {
+@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? type = null,Object? name = null,Object? nick = null,Object? bio = null,Object? picture = freezed,Object? background = freezed,Object? account = freezed,Object? accountId = freezed,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? realmId = freezed,Object? verification = freezed,}) {
   return _then(_self.copyWith(
 id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
 as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
@@ -437,9 +437,9 @@ as String,picture: freezed == picture ? _self.picture : picture // ignore: cast_
 as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable
 as SnCloudFile?,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
 as SnAccount?,accountId: freezed == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
-as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
-as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
-as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
+as String?,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
+as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
+as DateTime?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
 as DateTime?,realmId: freezed == realmId ? _self.realmId : realmId // ignore: cast_nullable_to_non_nullable
 as String?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable
 as SnVerificationMark?,
@@ -501,20 +501,20 @@ $SnVerificationMarkCopyWith<$Res>? get verification {
 @JsonSerializable()
 
 class _SnPublisher implements SnPublisher {
-  const _SnPublisher({required this.id, required this.type, required this.name, required this.nick, this.bio = '', required this.picture, required this.background, required this.account, required this.accountId, required this.createdAt, required this.updatedAt, required this.deletedAt, required this.realmId, required this.verification});
+  const _SnPublisher({this.id = '', this.type = 0, this.name = '', this.nick = '', this.bio = '', this.picture, this.background, this.account, this.accountId, this.createdAt = null, this.updatedAt = null, this.deletedAt, this.realmId, this.verification});
   factory _SnPublisher.fromJson(Map<String, dynamic> json) => _$SnPublisherFromJson(json);
 
-@override final  String id;
-@override final  int type;
-@override final  String name;
-@override final  String nick;
+@override@JsonKey() final  String id;
+@override@JsonKey() final  int type;
+@override@JsonKey() final  String name;
+@override@JsonKey() final  String nick;
 @override@JsonKey() final  String bio;
 @override final  SnCloudFile? picture;
 @override final  SnCloudFile? background;
 @override final  SnAccount? account;
 @override final  String? accountId;
-@override final  DateTime createdAt;
-@override final  DateTime updatedAt;
+@override@JsonKey() final  DateTime? createdAt;
+@override@JsonKey() final  DateTime? updatedAt;
 @override final  DateTime? deletedAt;
 @override final  String? realmId;
 @override final  SnVerificationMark? verification;
@@ -552,7 +552,7 @@ abstract mixin class _$SnPublisherCopyWith<$Res> implements $SnPublisherCopyWith
   factory _$SnPublisherCopyWith(_SnPublisher value, $Res Function(_SnPublisher) _then) = __$SnPublisherCopyWithImpl;
 @override @useResult
 $Res call({
- String id, int type, String name, String nick, String bio, SnCloudFile? picture, SnCloudFile? background, SnAccount? account, String? accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String? realmId, SnVerificationMark? verification
+ String id, int type, String name, String nick, String bio, SnCloudFile? picture, SnCloudFile? background, SnAccount? account, String? accountId, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String? realmId, SnVerificationMark? verification
 });
 
 
@@ -569,7 +569,7 @@ class __$SnPublisherCopyWithImpl<$Res>
 
 /// Create a copy of SnPublisher
 /// with the given fields replaced by the non-null parameter values.
-@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? type = null,Object? name = null,Object? nick = null,Object? bio = null,Object? picture = freezed,Object? background = freezed,Object? account = freezed,Object? accountId = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? realmId = freezed,Object? verification = freezed,}) {
+@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? type = null,Object? name = null,Object? nick = null,Object? bio = null,Object? picture = freezed,Object? background = freezed,Object? account = freezed,Object? accountId = freezed,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? realmId = freezed,Object? verification = freezed,}) {
   return _then(_SnPublisher(
 id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
 as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
@@ -580,9 +580,9 @@ as String,picture: freezed == picture ? _self.picture : picture // ignore: cast_
 as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable
 as SnCloudFile?,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
 as SnAccount?,accountId: freezed == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
-as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
-as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
-as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
+as String?,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
+as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
+as DateTime?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
 as DateTime?,realmId: freezed == realmId ? _self.realmId : realmId // ignore: cast_nullable_to_non_nullable
 as String?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable
 as SnVerificationMark?,
diff --git a/lib/models/post.g.dart b/lib/models/post.g.dart
index 18a9226..22b2304 100644
--- a/lib/models/post.g.dart
+++ b/lib/models/post.g.dart
@@ -15,16 +15,19 @@ _SnPost _$SnPostFromJson(Map<String, dynamic> json) => _SnPost(
       json['edited_at'] == null
           ? null
           : DateTime.parse(json['edited_at'] as String),
-  publishedAt: DateTime.parse(json['published_at'] as String),
-  visibility: (json['visibility'] as num).toInt(),
+  publishedAt:
+      json['published_at'] == null
+          ? null
+          : DateTime.parse(json['published_at'] as String),
+  visibility: (json['visibility'] as num?)?.toInt() ?? 0,
   content: json['content'] as String?,
-  type: (json['type'] as num).toInt(),
+  type: (json['type'] as num?)?.toInt() ?? 0,
   meta: json['meta'] as Map<String, dynamic>?,
-  viewsUnique: (json['views_unique'] as num).toInt(),
-  viewsTotal: (json['views_total'] as num).toInt(),
-  upvotes: (json['upvotes'] as num).toInt(),
-  downvotes: (json['downvotes'] as num).toInt(),
-  repliesCount: (json['replies_count'] as num).toInt(),
+  viewsUnique: (json['views_unique'] as num?)?.toInt() ?? 0,
+  viewsTotal: (json['views_total'] as num?)?.toInt() ?? 0,
+  upvotes: (json['upvotes'] as num?)?.toInt() ?? 0,
+  downvotes: (json['downvotes'] as num?)?.toInt() ?? 0,
+  repliesCount: (json['replies_count'] as num?)?.toInt() ?? 0,
   threadedPostId: json['threaded_post_id'] as String?,
   threadedPost:
       json['threaded_post'] == null
@@ -41,21 +44,31 @@ _SnPost _$SnPostFromJson(Map<String, dynamic> json) => _SnPost(
           ? null
           : SnPost.fromJson(json['forwarded_post'] as Map<String, dynamic>),
   attachments:
-      (json['attachments'] as List<dynamic>)
-          .map((e) => SnCloudFile.fromJson(e as Map<String, dynamic>))
-          .toList(),
-  publisher: SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
+      (json['attachments'] as List<dynamic>?)
+          ?.map((e) => SnCloudFile.fromJson(e as Map<String, dynamic>))
+          .toList() ??
+      const [],
+  publisher:
+      json['publisher'] == null
+          ? const SnPublisher()
+          : SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
   reactionsCount:
       (json['reactions_count'] as Map<String, dynamic>?)?.map(
         (k, e) => MapEntry(k, (e as num).toInt()),
       ) ??
       const {},
-  reactions: json['reactions'] as List<dynamic>,
-  tags: json['tags'] as List<dynamic>,
-  categories: json['categories'] as List<dynamic>,
-  collections: json['collections'] as List<dynamic>,
-  createdAt: DateTime.parse(json['created_at'] as String),
-  updatedAt: DateTime.parse(json['updated_at'] as String),
+  reactions: json['reactions'] as List<dynamic>? ?? const [],
+  tags: json['tags'] as List<dynamic>? ?? const [],
+  categories: json['categories'] as List<dynamic>? ?? const [],
+  collections: json['collections'] as List<dynamic>? ?? const [],
+  createdAt:
+      json['created_at'] == null
+          ? null
+          : DateTime.parse(json['created_at'] as String),
+  updatedAt:
+      json['updated_at'] == null
+          ? null
+          : DateTime.parse(json['updated_at'] as String),
   deletedAt:
       json['deleted_at'] == null
           ? null
@@ -69,7 +82,7 @@ Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
   'description': instance.description,
   'language': instance.language,
   'edited_at': instance.editedAt?.toIso8601String(),
-  'published_at': instance.publishedAt.toIso8601String(),
+  'published_at': instance.publishedAt?.toIso8601String(),
   'visibility': instance.visibility,
   'content': instance.content,
   'type': instance.type,
@@ -92,17 +105,17 @@ Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
   'tags': instance.tags,
   'categories': instance.categories,
   'collections': instance.collections,
-  'created_at': instance.createdAt.toIso8601String(),
-  'updated_at': instance.updatedAt.toIso8601String(),
+  'created_at': instance.createdAt?.toIso8601String(),
+  'updated_at': instance.updatedAt?.toIso8601String(),
   'deleted_at': instance.deletedAt?.toIso8601String(),
   'is_truncated': instance.isTruncated,
 };
 
 _SnPublisher _$SnPublisherFromJson(Map<String, dynamic> json) => _SnPublisher(
-  id: json['id'] as String,
-  type: (json['type'] as num).toInt(),
-  name: json['name'] as String,
-  nick: json['nick'] as String,
+  id: json['id'] as String? ?? '',
+  type: (json['type'] as num?)?.toInt() ?? 0,
+  name: json['name'] as String? ?? '',
+  nick: json['nick'] as String? ?? '',
   bio: json['bio'] as String? ?? '',
   picture:
       json['picture'] == null
@@ -117,8 +130,14 @@ _SnPublisher _$SnPublisherFromJson(Map<String, dynamic> json) => _SnPublisher(
           ? null
           : SnAccount.fromJson(json['account'] as Map<String, dynamic>),
   accountId: json['account_id'] as String?,
-  createdAt: DateTime.parse(json['created_at'] as String),
-  updatedAt: DateTime.parse(json['updated_at'] as String),
+  createdAt:
+      json['created_at'] == null
+          ? null
+          : DateTime.parse(json['created_at'] as String),
+  updatedAt:
+      json['updated_at'] == null
+          ? null
+          : DateTime.parse(json['updated_at'] as String),
   deletedAt:
       json['deleted_at'] == null
           ? null
@@ -143,8 +162,8 @@ Map<String, dynamic> _$SnPublisherToJson(_SnPublisher instance) =>
       'background': instance.background?.toJson(),
       'account': instance.account?.toJson(),
       'account_id': instance.accountId,
-      'created_at': instance.createdAt.toIso8601String(),
-      'updated_at': instance.updatedAt.toIso8601String(),
+      'created_at': instance.createdAt?.toIso8601String(),
+      'updated_at': instance.updatedAt?.toIso8601String(),
       'deleted_at': instance.deletedAt?.toIso8601String(),
       'realm_id': instance.realmId,
       'verification': instance.verification?.toJson(),
diff --git a/lib/pods/theme.dart b/lib/pods/theme.dart
index 0380245..421ecc1 100644
--- a/lib/pods/theme.dart
+++ b/lib/pods/theme.dart
@@ -102,6 +102,7 @@ Future<ThemeData> createAppTheme(
     ),
     snackBarTheme: SnackBarThemeData(
       behavior: useM3 ? SnackBarBehavior.floating : SnackBarBehavior.fixed,
+      width: 560,
     ),
     appBarTheme: AppBarTheme(
       centerTitle: true,
diff --git a/lib/screens/account/event_calendar.dart b/lib/screens/account/event_calendar.dart
index a4046c5..83093f4 100644
--- a/lib/screens/account/event_calendar.dart
+++ b/lib/screens/account/event_calendar.dart
@@ -61,7 +61,7 @@ class EventCalanderScreen extends HookConsumerWidget {
                   child: Column(
                     children: [
                       Card(
-                        margin: EdgeInsets.all(16),
+                        margin: EdgeInsets.only(left: 16, right: 16, top: 16),
                         child: Column(
                           children: [
                             // Use the reusable EventCalendarWidget
@@ -77,7 +77,6 @@ class EventCalanderScreen extends HookConsumerWidget {
                       ),
 
                       // Add the fortune graph widget
-                      const Divider(height: 1),
                       FortuneGraphWidget(
                         events: events,
                         constrainWidth: true,
diff --git a/lib/screens/posts/compose.dart b/lib/screens/posts/compose.dart
index 60b057a..c9d738e 100644
--- a/lib/screens/posts/compose.dart
+++ b/lib/screens/posts/compose.dart
@@ -62,8 +62,6 @@ class PostComposeScreen extends HookConsumerWidget {
     @QueryParam('type') this.type,
   });
 
-
-
   @override
   Widget build(BuildContext context, WidgetRef ref) {
     // Determine the compose type: auto-detect from edited post or use query parameter
@@ -96,7 +94,7 @@ class PostComposeScreen extends HookConsumerWidget {
     useEffect(() {
       if (originalPost == null) {
         // Only auto-save for new posts, not edits
-        state.startAutoSave(ref);
+        state.startAutoSave(ref, postType: 0);
       }
       return () => state.stopAutoSave();
     }, [state]);
@@ -118,14 +116,14 @@ class PostComposeScreen extends HookConsumerWidget {
         final drafts = ref.read(composeStorageNotifierProvider);
         if (drafts.isNotEmpty) {
           final mostRecentDraft = drafts.values.reduce(
-            (a, b) => a.lastModified.isAfter(b.lastModified) ? a : b,
+            (a, b) => (a.updatedAt ?? DateTime(0)).isAfter(b.updatedAt ?? DateTime(0)) ? a : b,
           );
 
           // Only load if the draft has meaningful content
-          if (!mostRecentDraft.isEmpty) {
-            state.titleController.text = mostRecentDraft.title;
-            state.descriptionController.text = mostRecentDraft.description;
-            state.contentController.text = mostRecentDraft.content;
+          if (mostRecentDraft.content?.isNotEmpty == true || mostRecentDraft.title?.isNotEmpty == true) {
+            state.titleController.text = mostRecentDraft.title ?? '';
+            state.descriptionController.text = mostRecentDraft.description ?? '';
+            state.contentController.text = mostRecentDraft.content ?? '';
             state.visibility.value = mostRecentDraft.visibility;
           }
         }
@@ -162,9 +160,10 @@ class PostComposeScreen extends HookConsumerWidget {
     Widget buildWideAttachmentGrid() {
       return GridView.builder(
         shrinkWrap: true,
+        padding: EdgeInsets.zero,
         physics: const NeverScrollableScrollPhysics(),
         gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
-          crossAxisCount: 3,
+          crossAxisCount: 2,
           crossAxisSpacing: 8,
           mainAxisSpacing: 8,
         ),
@@ -245,17 +244,16 @@ class PostComposeScreen extends HookConsumerWidget {
                     isScrollControlled: true,
                     builder:
                         (context) => DraftManagerSheet(
-                          isArticle: false,
                           onDraftSelected: (draftId) {
                             final draft =
                                 ref.read(
                                   composeStorageNotifierProvider,
                                 )[draftId];
                             if (draft != null) {
-                              state.titleController.text = draft.title;
+                              state.titleController.text = draft.title ?? '';
                               state.descriptionController.text =
-                                  draft.description;
-                              state.contentController.text = draft.content;
+                                  draft.description ?? '';
+                              state.contentController.text = draft.content ?? '';
                               state.visibility.value = draft.visibility;
                             }
                           },
@@ -320,7 +318,7 @@ class PostComposeScreen extends HookConsumerWidget {
             // Main content area
             Expanded(
               child: ConstrainedBox(
-                constraints: const BoxConstraints(maxWidth: 480),
+                constraints: const BoxConstraints(maxWidth: 560),
                 child: Row(
                   spacing: 12,
                   crossAxisAlignment: CrossAxisAlignment.start,
diff --git a/lib/screens/posts/compose_article.dart b/lib/screens/posts/compose_article.dart
index 560a257..421111d 100644
--- a/lib/screens/posts/compose_article.dart
+++ b/lib/screens/posts/compose_article.dart
@@ -1,5 +1,5 @@
 import 'dart:async';
-import 'dart:developer';
+
 import 'package:auto_route/auto_route.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
@@ -9,6 +9,7 @@ import 'package:gap/gap.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:island/models/file.dart';
 import 'package:island/models/post.dart';
+
 import 'package:island/screens/creators/publishers.dart';
 import 'package:island/services/responsive.dart';
 import 'package:island/widgets/app_scaffold.dart';
@@ -21,6 +22,7 @@ import 'package:island/widgets/post/compose_settings_sheet.dart';
 import 'package:island/services/compose_storage_db.dart';
 import 'package:island/widgets/post/publishers_modal.dart';
 import 'package:island/widgets/post/draft_manager.dart';
+
 import 'package:material_symbols_icons/symbols.dart';
 import 'package:styled_widget/styled_widget.dart';
 
@@ -71,7 +73,7 @@ class ArticleComposeScreen extends HookConsumerWidget {
       if (originalPost == null) {
         // Only auto-save for new articles, not edits
         autoSaveTimer = Timer.periodic(const Duration(seconds: 3), (_) {
-          _saveArticleDraft(ref, state);
+          ComposeLogic.saveDraftWithoutUpload(ref, state, postType: 1);
         });
       }
       return () {
@@ -79,7 +81,7 @@ class ArticleComposeScreen extends HookConsumerWidget {
         state.stopAutoSave();
         // Save final draft before disposing
         if (originalPost == null) {
-          _saveArticleDraft(ref, state);
+          ComposeLogic.saveDraftWithoutUpload(ref, state, postType: 1);
         }
         ComposeLogic.dispose(state);
         autoSaveTimer?.cancel();
@@ -100,17 +102,22 @@ class ArticleComposeScreen extends HookConsumerWidget {
     useEffect(() {
       if (originalPost == null) {
         // Try to load the most recent article draft
-        final drafts = ref.read(articleStorageNotifierProvider);
+        final drafts = ref.read(composeStorageNotifierProvider);
         if (drafts.isNotEmpty) {
           final mostRecentDraft = drafts.values.reduce(
-            (a, b) => a.lastModified.isAfter(b.lastModified) ? a : b,
+            (a, b) =>
+                (a.updatedAt ?? DateTime(0)).isAfter(b.updatedAt ?? DateTime(0))
+                    ? a
+                    : b,
           );
 
           // Only load if the draft has meaningful content
-          if (!mostRecentDraft.isEmpty) {
-            state.titleController.text = mostRecentDraft.title;
-            state.descriptionController.text = mostRecentDraft.description;
-            state.contentController.text = mostRecentDraft.content;
+          if (mostRecentDraft.content?.isNotEmpty == true ||
+              mostRecentDraft.title?.isNotEmpty == true) {
+            state.titleController.text = mostRecentDraft.title ?? '';
+            state.descriptionController.text =
+                mostRecentDraft.description ?? '';
+            state.contentController.text = mostRecentDraft.content ?? '';
             state.visibility.value = mostRecentDraft.visibility;
           }
         }
@@ -356,7 +363,7 @@ class ArticleComposeScreen extends HookConsumerWidget {
     return PopScope(
       onPopInvoked: (_) {
         if (originalPost == null) {
-          _saveArticleDraft(ref, state);
+          ComposeLogic.saveDraftWithoutUpload(ref, state, postType: 1);
         }
       },
       child: AppScaffold(
@@ -383,17 +390,17 @@ class ArticleComposeScreen extends HookConsumerWidget {
                     isScrollControlled: true,
                     builder:
                         (context) => DraftManagerSheet(
-                          isArticle: true,
                           onDraftSelected: (draftId) {
                             final draft =
                                 ref.read(
-                                  articleStorageNotifierProvider,
+                                  composeStorageNotifierProvider,
                                 )[draftId];
                             if (draft != null) {
-                              state.titleController.text = draft.title;
+                              state.titleController.text = draft.title ?? '';
                               state.descriptionController.text =
-                                  draft.description;
-                              state.contentController.text = draft.content;
+                                  draft.description ?? '';
+                              state.contentController.text =
+                                  draft.content ?? '';
                               state.visibility.value = draft.visibility;
                             }
                           },
@@ -404,7 +411,7 @@ class ArticleComposeScreen extends HookConsumerWidget {
               ),
             IconButton(
               icon: const Icon(Symbols.save),
-              onPressed: () => _saveArticleDraft(ref, state),
+              onPressed: () => ComposeLogic.saveDraft(ref, state, postType: 1),
               tooltip: 'saveDraft'.tr(),
             ),
             IconButton(
@@ -524,7 +531,7 @@ class ArticleComposeScreen extends HookConsumerWidget {
     if (isPaste && isModifierPressed) {
       ComposeLogic.handlePaste(state);
     } else if (isSave && isModifierPressed) {
-      _saveArticleDraft(ref, state);
+      ComposeLogic.saveDraft(ref, state, postType: 1);
     } else if (isSubmit && isModifierPressed && !state.submitting.value) {
       ComposeLogic.performAction(
         ref,
@@ -537,23 +544,5 @@ class ArticleComposeScreen extends HookConsumerWidget {
   }
 
   // Helper method to save article draft
-  Future<void> _saveArticleDraft(WidgetRef ref, ComposeState state) async {
-    try {
-      final draft = ArticleDraftModel(
-        id: state.draftId,
-        title: state.titleController.text,
-        description: state.descriptionController.text,
-        content: state.contentController.text,
-        visibility: state.visibility.value,
-        lastModified: DateTime.now(),
-      );
-
-      await ref.read(articleStorageNotifierProvider.notifier).saveDraft(draft);
-    } catch (e) {
-      log('[ArticleCompose] Failed to save draft, error: $e');
-      // Silently fail for auto-save to avoid disrupting user experience
-    }
-  }
-
 
 }
diff --git a/lib/services/compose_storage_db.dart b/lib/services/compose_storage_db.dart
index fdf8110..121994d 100644
--- a/lib/services/compose_storage_db.dart
+++ b/lib/services/compose_storage_db.dart
@@ -1,183 +1,16 @@
 import 'dart:convert';
 import 'package:drift/drift.dart';
-import 'package:riverpod_annotation/riverpod_annotation.dart';
 import 'package:island/database/drift_db.dart';
+import 'package:island/models/post.dart';
 import 'package:island/pods/database.dart';
-import 'package:island/services/file.dart';
-import 'package:island/models/file.dart';
-import 'package:island/pods/config.dart';
-import 'package:island/pods/network.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
 
 part 'compose_storage_db.g.dart';
 
-class ComposeDraftModel {
-  final String id;
-  final String title;
-  final String description;
-  final String content;
-  final List<UniversalFile> attachments;
-  final int visibility;
-  final DateTime lastModified;
-
-  ComposeDraftModel({
-    required this.id,
-    required this.title,
-    required this.description,
-    required this.content,
-    required this.attachments,
-    required this.visibility,
-    required this.lastModified,
-  });
-
-  Map<String, dynamic> toJson() => {
-    'id': id,
-    'title': title,
-    'description': description,
-    'content': content,
-    'attachments': attachments.map((e) => e.toJson()).toList(),
-    'visibility': visibility,
-    'lastModified': lastModified.toIso8601String(),
-  };
-
-  factory ComposeDraftModel.fromJson(Map<String, dynamic> json) => ComposeDraftModel(
-    id: json['id'] as String,
-    title: json['title'] as String? ?? '',
-    description: json['description'] as String? ?? '',
-    content: json['content'] as String? ?? '',
-    attachments: (json['attachments'] as List? ?? [])
-        .map((e) => UniversalFile.fromJson(e as Map<String, dynamic>))
-        .toList(),
-    visibility: json['visibility'] as int? ?? 0,
-    lastModified: DateTime.parse(json['lastModified'] as String),
-  );
-
-  factory ComposeDraftModel.fromDbRow(ComposeDraft row) => ComposeDraftModel(
-    id: row.id,
-    title: row.title,
-    description: row.description,
-    content: row.content,
-    attachments: (jsonDecode(row.attachmentIds) as List)
-        .map((e) => UniversalFile.fromJson(e as Map<String, dynamic>))
-        .toList(),
-    visibility: row.visibility,
-    lastModified: row.lastModified,
-  );
-
-  ComposeDraftsCompanion toDbCompanion() => ComposeDraftsCompanion(
-    id: Value(id),
-    title: Value(title),
-    description: Value(description),
-    content: Value(content),
-    attachmentIds: Value(jsonEncode(attachments.map((e) => e.toJson()).toList())),
-    visibility: Value(visibility),
-    lastModified: Value(lastModified),
-  );
-
-  ComposeDraftModel copyWith({
-    String? id,
-    String? title,
-    String? description,
-    String? content,
-    List<UniversalFile>? attachments,
-    int? visibility,
-    DateTime? lastModified,
-  }) {
-    return ComposeDraftModel(
-      id: id ?? this.id,
-      title: title ?? this.title,
-      description: description ?? this.description,
-      content: content ?? this.content,
-      attachments: attachments ?? this.attachments,
-      visibility: visibility ?? this.visibility,
-      lastModified: lastModified ?? this.lastModified,
-    );
-  }
-
-  bool get isEmpty =>
-      title.isEmpty &&
-      description.isEmpty &&
-      content.isEmpty &&
-      attachments.isEmpty;
-}
-
-class ArticleDraftModel {
-  final String id;
-  final String title;
-  final String description;
-  final String content;
-  final int visibility;
-  final DateTime lastModified;
-
-  ArticleDraftModel({
-    required this.id,
-    required this.title,
-    required this.description,
-    required this.content,
-    required this.visibility,
-    required this.lastModified,
-  });
-
-  Map<String, dynamic> toJson() => {
-    'id': id,
-    'title': title,
-    'description': description,
-    'content': content,
-    'visibility': visibility,
-    'lastModified': lastModified.toIso8601String(),
-  };
-
-  factory ArticleDraftModel.fromJson(Map<String, dynamic> json) => ArticleDraftModel(
-    id: json['id'] as String,
-    title: json['title'] as String? ?? '',
-    description: json['description'] as String? ?? '',
-    content: json['content'] as String? ?? '',
-    visibility: json['visibility'] as int? ?? 0,
-    lastModified: DateTime.parse(json['lastModified'] as String),
-  );
-
-  factory ArticleDraftModel.fromDbRow(ArticleDraft row) => ArticleDraftModel(
-    id: row.id,
-    title: row.title,
-    description: row.description,
-    content: row.content,
-    visibility: row.visibility,
-    lastModified: row.lastModified,
-  );
-
-  ArticleDraftsCompanion toDbCompanion() => ArticleDraftsCompanion(
-    id: Value(id),
-    title: Value(title),
-    description: Value(description),
-    content: Value(content),
-    visibility: Value(visibility),
-    lastModified: Value(lastModified),
-  );
-
-  ArticleDraftModel copyWith({
-    String? id,
-    String? title,
-    String? description,
-    String? content,
-    int? visibility,
-    DateTime? lastModified,
-  }) {
-    return ArticleDraftModel(
-      id: id ?? this.id,
-      title: title ?? this.title,
-      description: description ?? this.description,
-      content: content ?? this.content,
-      visibility: visibility ?? this.visibility,
-      lastModified: lastModified ?? this.lastModified,
-    );
-  }
-
-  bool get isEmpty => title.isEmpty && description.isEmpty && content.isEmpty;
-}
-
 @riverpod
 class ComposeStorageNotifier extends _$ComposeStorageNotifier {
   @override
-  Map<String, ComposeDraftModel> build() {
+  Map<String, SnPost> build() {
     _loadDrafts();
     return {};
   }
@@ -185,10 +18,9 @@ class ComposeStorageNotifier extends _$ComposeStorageNotifier {
   void _loadDrafts() async {
     try {
       final database = ref.read(databaseProvider);
-      final dbDrafts = await database.getAllComposeDrafts();
-      final drafts = <String, ComposeDraftModel>{};
-      for (final dbDraft in dbDrafts) {
-        final draft = ComposeDraftModel.fromDbRow(dbDraft);
+      final dbDrafts = await database.getAllPostDrafts();
+      final drafts = <String, SnPost>{};
+      for (final draft in dbDrafts) {
         drafts[draft.id] = draft;
       }
       state = drafts;
@@ -198,52 +30,22 @@ class ComposeStorageNotifier extends _$ComposeStorageNotifier {
     }
   }
 
-  Future<void> saveDraft(ComposeDraftModel draft) async {
-    if (draft.isEmpty) {
-      await deleteDraft(draft.id);
-      return;
-    }
-
-    // Upload all attachments that are not yet uploaded
-    final uploadedAttachments = <UniversalFile>[];
-    final serverUrl = ref.read(serverUrlProvider);
-    final token = ref.read(tokenProvider);
-    
-    for (final attachment in draft.attachments) {
-      if (!attachment.isOnCloud) {
-        try {
-          final completer = putMediaToCloud(
-            fileData: attachment,
-            atk: token?.token ?? '',
-            baseUrl: serverUrl,
-          );
-          final uploadedFile = await completer.future;
-          if (uploadedFile != null) {
-            uploadedAttachments.add(UniversalFile.fromAttachment(uploadedFile));
-          } else {
-            uploadedAttachments.add(attachment);
-          }
-        } catch (e) {
-          // If upload fails, keep the original file
-          uploadedAttachments.add(attachment);
-        }
-      } else {
-        uploadedAttachments.add(attachment);
-      }
-    }
-
-    final updatedDraft = draft.copyWith(
-      attachments: uploadedAttachments,
-      lastModified: DateTime.now(),
-    );
+  Future<void> saveDraft(SnPost draft) async {
+    final updatedDraft = draft.copyWith(updatedAt: DateTime.now());
     state = {...state, updatedDraft.id: updatedDraft};
-    
+
     try {
       final database = ref.read(databaseProvider);
-      await database.saveComposeDraft(updatedDraft.toDbCompanion());
+      await database.addPostDraft(
+        PostDraftsCompanion(
+          id: Value(updatedDraft.id),
+          post: Value(jsonEncode(updatedDraft.toJson())),
+          lastModified: Value(updatedDraft.updatedAt ?? DateTime.now()),
+        ),
+      );
     } catch (e) {
       // Revert state on error
-      final newState = Map<String, ComposeDraftModel>.from(state);
+      final newState = Map<String, SnPost>.from(state);
       newState.remove(updatedDraft.id);
       state = newState;
       rethrow;
@@ -252,13 +54,13 @@ class ComposeStorageNotifier extends _$ComposeStorageNotifier {
 
   Future<void> deleteDraft(String id) async {
     final oldDraft = state[id];
-    final newState = Map<String, ComposeDraftModel>.from(state);
+    final newState = Map<String, SnPost>.from(state);
     newState.remove(id);
     state = newState;
-    
+
     try {
       final database = ref.read(databaseProvider);
-      await database.deleteComposeDraft(id);
+      await database.deletePostDraft(id);
     } catch (e) {
       // Revert state on error
       if (oldDraft != null) {
@@ -268,22 +70,22 @@ class ComposeStorageNotifier extends _$ComposeStorageNotifier {
     }
   }
 
-  ComposeDraftModel? getDraft(String id) {
+  SnPost? getDraft(String id) {
     return state[id];
   }
 
-  List<ComposeDraftModel> getAllDrafts() {
+  List<SnPost> getAllDrafts() {
     final drafts = state.values.toList();
-    drafts.sort((a, b) => b.lastModified.compareTo(a.lastModified));
+    drafts.sort((a, b) => b.updatedAt!.compareTo(a.updatedAt!));
     return drafts;
   }
 
   Future<void> clearAllDrafts() async {
     state = {};
-    
+
     try {
       final database = ref.read(databaseProvider);
-      await database.clearAllComposeDrafts();
+      await database.clearAllPostDrafts();
     } catch (e) {
       // If clearing fails, we might want to reload from database
       _loadDrafts();
@@ -291,90 +93,3 @@ class ComposeStorageNotifier extends _$ComposeStorageNotifier {
     }
   }
 }
-
-@riverpod
-class ArticleStorageNotifier extends _$ArticleStorageNotifier {
-  @override
-  Map<String, ArticleDraftModel> build() {
-    _loadDrafts();
-    return {};
-  }
-
-  void _loadDrafts() async {
-    try {
-      final database = ref.read(databaseProvider);
-      final dbDrafts = await database.getAllArticleDrafts();
-      final drafts = <String, ArticleDraftModel>{};
-      for (final dbDraft in dbDrafts) {
-        final draft = ArticleDraftModel.fromDbRow(dbDraft);
-        drafts[draft.id] = draft;
-      }
-      state = drafts;
-    } catch (e) {
-      // If there's an error loading drafts, start with empty state
-      state = {};
-    }
-  }
-
-  Future<void> saveDraft(ArticleDraftModel draft) async {
-    if (draft.isEmpty) {
-      await deleteDraft(draft.id);
-      return;
-    }
-
-    final updatedDraft = draft.copyWith(lastModified: DateTime.now());
-    state = {...state, updatedDraft.id: updatedDraft};
-    
-    try {
-      final database = ref.read(databaseProvider);
-      await database.saveArticleDraft(updatedDraft.toDbCompanion());
-    } catch (e) {
-      // Revert state on error
-      final newState = Map<String, ArticleDraftModel>.from(state);
-      newState.remove(updatedDraft.id);
-      state = newState;
-      rethrow;
-    }
-  }
-
-  Future<void> deleteDraft(String id) async {
-    final oldDraft = state[id];
-    final newState = Map<String, ArticleDraftModel>.from(state);
-    newState.remove(id);
-    state = newState;
-    
-    try {
-      final database = ref.read(databaseProvider);
-      await database.deleteArticleDraft(id);
-    } catch (e) {
-      // Revert state on error
-      if (oldDraft != null) {
-        state = {...state, id: oldDraft};
-      }
-      rethrow;
-    }
-  }
-
-  ArticleDraftModel? getDraft(String id) {
-    return state[id];
-  }
-
-  List<ArticleDraftModel> getAllDrafts() {
-    final drafts = state.values.toList();
-    drafts.sort((a, b) => b.lastModified.compareTo(a.lastModified));
-    return drafts;
-  }
-
-  Future<void> clearAllDrafts() async {
-    state = {};
-    
-    try {
-      final database = ref.read(databaseProvider);
-      await database.clearAllArticleDrafts();
-    } catch (e) {
-      // If clearing fails, we might want to reload from database
-      _loadDrafts();
-      rethrow;
-    }
-  }
-}
\ No newline at end of file
diff --git a/lib/services/compose_storage_db.g.dart b/lib/services/compose_storage_db.g.dart
index 654fafe..051438a 100644
--- a/lib/services/compose_storage_db.g.dart
+++ b/lib/services/compose_storage_db.g.dart
@@ -7,13 +7,13 @@ part of 'compose_storage_db.dart';
 // **************************************************************************
 
 String _$composeStorageNotifierHash() =>
-    r'fcdb006dca44d30916a20804922e93d0caad49ca';
+    r'3de7a01a93d999d45a32fb68617b77f194589686';
 
 /// See also [ComposeStorageNotifier].
 @ProviderFor(ComposeStorageNotifier)
 final composeStorageNotifierProvider = AutoDisposeNotifierProvider<
   ComposeStorageNotifier,
-  Map<String, ComposeDraftModel>
+  Map<String, SnPost>
 >.internal(
   ComposeStorageNotifier.new,
   name: r'composeStorageNotifierProvider',
@@ -25,28 +25,6 @@ final composeStorageNotifierProvider = AutoDisposeNotifierProvider<
   allTransitiveDependencies: null,
 );
 
-typedef _$ComposeStorageNotifier =
-    AutoDisposeNotifier<Map<String, ComposeDraftModel>>;
-String _$articleStorageNotifierHash() =>
-    r'21ee0f8ee87528bebf8f5f4b0b2892cd8058e230';
-
-/// See also [ArticleStorageNotifier].
-@ProviderFor(ArticleStorageNotifier)
-final articleStorageNotifierProvider = AutoDisposeNotifierProvider<
-  ArticleStorageNotifier,
-  Map<String, ArticleDraftModel>
->.internal(
-  ArticleStorageNotifier.new,
-  name: r'articleStorageNotifierProvider',
-  debugGetCreateSourceHash:
-      const bool.fromEnvironment('dart.vm.product')
-          ? null
-          : _$articleStorageNotifierHash,
-  dependencies: null,
-  allTransitiveDependencies: null,
-);
-
-typedef _$ArticleStorageNotifier =
-    AutoDisposeNotifier<Map<String, ArticleDraftModel>>;
+typedef _$ComposeStorageNotifier = AutoDisposeNotifier<Map<String, SnPost>>;
 // ignore_for_file: type=lint
 // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
diff --git a/lib/widgets/post/compose_shared.dart b/lib/widgets/post/compose_shared.dart
index 238e90c..8d25b3c 100644
--- a/lib/widgets/post/compose_shared.dart
+++ b/lib/widgets/post/compose_shared.dart
@@ -1,7 +1,6 @@
-import 'dart:developer';
-
 import 'package:collection/collection.dart';
 import 'package:dio/dio.dart';
+import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -15,6 +14,7 @@ import 'package:island/services/compose_storage_db.dart';
 import 'package:island/widgets/alert.dart';
 import 'package:pasteboard/pasteboard.dart';
 import 'dart:async';
+import 'dart:developer';
 
 class ComposeState {
   final ValueNotifier<List<UniversalFile>> attachments;
@@ -40,10 +40,10 @@ class ComposeState {
     required this.draftId,
   });
 
-  void startAutoSave(WidgetRef ref) {
+  void startAutoSave(WidgetRef ref, {int postType = 0}) {
     _autoSaveTimer?.cancel();
     _autoSaveTimer = Timer.periodic(const Duration(seconds: 3), (_) {
-      ComposeLogic.saveDraft(ref, this);
+      ComposeLogic.saveDraftWithoutUpload(ref, this, postType: postType);
     });
   }
 
@@ -96,9 +96,11 @@ class ComposeLogic {
     );
   }
 
-  static ComposeState createStateFromDraft(ComposeDraftModel draft) {
+  static ComposeState createStateFromDraft(SnPost draft) {
     return ComposeState(
-      attachments: ValueNotifier<List<UniversalFile>>([]),
+      attachments: ValueNotifier<List<UniversalFile>>(
+        draft.attachments.map((e) => UniversalFile.fromAttachment(e)).toList(),
+      ),
       titleController: TextEditingController(text: draft.title),
       descriptionController: TextEditingController(text: draft.description),
       contentController: TextEditingController(text: draft.content),
@@ -110,29 +112,247 @@ class ComposeLogic {
     );
   }
 
+  static Future<void> saveDraft(WidgetRef ref, ComposeState state, {int postType = 0}) async {
+    final hasContent =
+        state.titleController.text.trim().isNotEmpty ||
+        state.descriptionController.text.trim().isNotEmpty ||
+        state.contentController.text.trim().isNotEmpty;
+    final hasAttachments = state.attachments.value.isNotEmpty;
 
+    if (!hasContent && !hasAttachments) {
+      return; // Don't save empty posts
+    }
 
-  static Future<void> saveDraft(WidgetRef ref, ComposeState state) async {
     try {
-      // Check if the auto-save timer is still active (widget not disposed)
       if (state._autoSaveTimer == null) {
-        return; // Widget has been disposed, don't save
+        return;
       }
 
-      final draft = ComposeDraftModel(
+      // Upload any local attachments first
+      final baseUrl = ref.watch(serverUrlProvider);
+      final token = await getToken(ref.watch(tokenProvider));
+      if (token == null) throw ArgumentError('Token is null');
+
+      for (int i = 0; i < state.attachments.value.length; i++) {
+        final attachment = state.attachments.value[i];
+        if (attachment.data is! SnCloudFile) {
+          try {
+            final cloudFile =
+                await putMediaToCloud(
+                  fileData: attachment,
+                  atk: token,
+                  baseUrl: baseUrl,
+                  filename: attachment.data.name ?? (postType == 1 ? 'Article media' : 'Post media'),
+                  mimetype:
+                      attachment.data.mimeType ??
+                      ComposeLogic.getMimeTypeFromFileType(attachment.type),
+                ).future;
+            if (cloudFile != null) {
+              // Update attachments list with cloud file
+              final clone = List.of(state.attachments.value);
+              clone[i] = UniversalFile(data: cloudFile, type: attachment.type);
+              state.attachments.value = clone;
+            }
+          } catch (err) {
+            log('[ComposeLogic] Failed to upload attachment: $err');
+            // Continue with other attachments even if one fails
+          }
+        }
+      }
+
+      final draft = SnPost(
         id: state.draftId,
         title: state.titleController.text,
         description: state.descriptionController.text,
-        content: state.contentController.text,
-        attachments: state.attachments.value,
+        language: null,
+        editedAt: null,
+        publishedAt: DateTime.now(),
         visibility: state.visibility.value,
-        lastModified: DateTime.now(),
+        content: state.contentController.text,
+        type: postType,
+        meta: null,
+        viewsUnique: 0,
+        viewsTotal: 0,
+        upvotes: 0,
+        downvotes: 0,
+        repliesCount: 0,
+        threadedPostId: null,
+        threadedPost: null,
+        repliedPostId: null,
+        repliedPost: null,
+        forwardedPostId: null,
+        forwardedPost: null,
+        attachments:
+            state.attachments.value
+                .map((e) => e.data)
+                .whereType<SnCloudFile>()
+                .toList(),
+        publisher: SnPublisher(
+          id: '',
+          type: 0,
+          name: '',
+          nick: '',
+          picture: null,
+          background: null,
+          account: null,
+          accountId: null,
+          createdAt: DateTime.now(),
+          updatedAt: DateTime.now(),
+          deletedAt: null,
+          realmId: null,
+          verification: null,
+        ),
+        reactions: [],
+        tags: [],
+        categories: [],
+        collections: [],
+        createdAt: DateTime.now(),
+        updatedAt: DateTime.now(),
+        deletedAt: null,
       );
 
       await ref.read(composeStorageNotifierProvider.notifier).saveDraft(draft);
     } catch (e) {
       log('[ComposeLogic] Failed to save draft, error: $e');
-      // Silently fail for auto-save to avoid disrupting user experience
+    }
+  }
+
+  static Future<void> saveDraftWithoutUpload(WidgetRef ref, ComposeState state, {int postType = 0}) async {
+    final hasContent =
+        state.titleController.text.trim().isNotEmpty ||
+        state.descriptionController.text.trim().isNotEmpty ||
+        state.contentController.text.trim().isNotEmpty;
+    final hasAttachments = state.attachments.value.isNotEmpty;
+
+    if (!hasContent && !hasAttachments) {
+      return; // Don't save empty posts
+    }
+
+    try {
+      if (state._autoSaveTimer == null) {
+        return;
+      }
+
+      final draft = SnPost(
+        id: state.draftId,
+        title: state.titleController.text,
+        description: state.descriptionController.text,
+        language: null,
+        editedAt: null,
+        publishedAt: DateTime.now(),
+        visibility: state.visibility.value,
+        content: state.contentController.text,
+        type: postType,
+        meta: null,
+        viewsUnique: 0,
+        viewsTotal: 0,
+        upvotes: 0,
+        downvotes: 0,
+        repliesCount: 0,
+        threadedPostId: null,
+        threadedPost: null,
+        repliedPostId: null,
+        repliedPost: null,
+        forwardedPostId: null,
+        forwardedPost: null,
+        attachments:
+            state.attachments.value
+                .map((e) => e.data)
+                .whereType<SnCloudFile>()
+                .toList(),
+        publisher: SnPublisher(
+          id: '',
+          type: 0,
+          name: '',
+          nick: '',
+          picture: null,
+          background: null,
+          account: null,
+          accountId: null,
+          createdAt: DateTime.now(),
+          updatedAt: DateTime.now(),
+          deletedAt: null,
+          realmId: null,
+          verification: null,
+        ),
+        reactions: [],
+        tags: [],
+        categories: [],
+        collections: [],
+        createdAt: DateTime.now(),
+        updatedAt: DateTime.now(),
+        deletedAt: null,
+      );
+
+      await ref.read(composeStorageNotifierProvider.notifier).saveDraft(draft);
+    } catch (e) {
+      log('[ComposeLogic] Failed to save draft without upload, error: $e');
+    }
+  }
+
+  static Future<void> saveDraftManually(
+    WidgetRef ref,
+    ComposeState state,
+    BuildContext context,
+  ) async {
+    try {
+      final draft = SnPost(
+        id: state.draftId,
+        title: state.titleController.text,
+        description: state.descriptionController.text,
+        language: null,
+        editedAt: null,
+        publishedAt: DateTime.now(),
+        visibility: state.visibility.value,
+        content: state.contentController.text,
+        type: 0,
+        meta: null,
+        viewsUnique: 0,
+        viewsTotal: 0,
+        upvotes: 0,
+        downvotes: 0,
+        repliesCount: 0,
+        threadedPostId: null,
+        threadedPost: null,
+        repliedPostId: null,
+        repliedPost: null,
+        forwardedPostId: null,
+        forwardedPost: null,
+        attachments: [], // TODO: Handle attachments
+        publisher: SnPublisher(
+          id: '',
+          type: 0,
+          name: '',
+          nick: '',
+          picture: null,
+          background: null,
+          account: null,
+          accountId: null,
+          createdAt: DateTime.now(),
+          updatedAt: DateTime.now(),
+          deletedAt: null,
+          realmId: null,
+          verification: null,
+        ),
+        reactions: [],
+        tags: [],
+        categories: [],
+        collections: [],
+        createdAt: DateTime.now(),
+        updatedAt: DateTime.now(),
+        deletedAt: null,
+      );
+
+      await ref.read(composeStorageNotifierProvider.notifier).saveDraft(draft);
+
+      if (context.mounted) {
+        showSnackBar(context, 'draftSaved'.tr());
+      }
+    } catch (e) {
+      log('[ComposeLogic] Failed to save draft manually, error: $e');
+      if (context.mounted) {
+        showSnackBar(context, 'draftSaveFailed'.tr());
+      }
     }
   }
 
@@ -146,7 +366,7 @@ class ComposeLogic {
     }
   }
 
-  static Future<ComposeDraftModel?> loadDraft(WidgetRef ref, String draftId) async {
+  static Future<SnPost?> loadDraft(WidgetRef ref, String draftId) async {
     try {
       return ref
           .read(composeStorageNotifierProvider.notifier)
@@ -282,6 +502,20 @@ class ComposeLogic {
   }) async {
     if (state.submitting.value) return;
 
+    // Don't submit empty posts (no content and no attachments)
+    final hasContent =
+        state.titleController.text.trim().isNotEmpty ||
+        state.descriptionController.text.trim().isNotEmpty ||
+        state.contentController.text.trim().isNotEmpty;
+    final hasAttachments = state.attachments.value.isNotEmpty;
+
+    if (!hasContent && !hasAttachments) {
+      if (context.mounted) {
+        showSnackBar(context, 'postContentEmpty'.tr());
+      }
+      return; // Don't submit empty posts
+    }
+
     try {
       state.submitting.value = true;
 
@@ -329,7 +563,7 @@ class ComposeLogic {
       if (postType == 1) {
         // Delete article draft
         await ref
-            .read(articleStorageNotifierProvider.notifier)
+            .read(composeStorageNotifierProvider.notifier)
             .deleteDraft(state.draftId);
       } else {
         // Delete regular post draft
@@ -381,7 +615,7 @@ class ComposeLogic {
     if (isPaste && isModifierPressed) {
       handlePaste(state);
     } else if (isSave && isModifierPressed) {
-      saveDraft(ref, state);
+      saveDraftManually(ref, state, context);
     } else if (isSubmit && isModifierPressed && !state.submitting.value) {
       performAction(
         ref,
diff --git a/lib/widgets/post/draft_manager.dart b/lib/widgets/post/draft_manager.dart
index 1a915ec..0784d03 100644
--- a/lib/widgets/post/draft_manager.dart
+++ b/lib/widgets/post/draft_manager.dart
@@ -7,208 +7,168 @@ import 'package:island/services/compose_storage_db.dart';
 import 'package:material_symbols_icons/symbols.dart';
 
 class DraftManagerSheet extends HookConsumerWidget {
-  final bool isArticle;
   final Function(String draftId)? onDraftSelected;
 
-  const DraftManagerSheet({
-    super.key,
-    this.isArticle = false,
-    this.onDraftSelected,
-  });
+  const DraftManagerSheet({super.key, this.onDraftSelected});
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
     final theme = Theme.of(context);
     final colorScheme = theme.colorScheme;
+    final isLoading = useState(true);
 
-    final drafts =
-        isArticle
-            ? ref.watch(articleStorageNotifierProvider)
-            : ref.watch(composeStorageNotifierProvider);
+    final drafts = ref.watch(composeStorageNotifierProvider);
 
-    final sortedDrafts = useMemoized(() {
-      if (isArticle) {
-        final draftList = drafts.values.cast<ArticleDraftModel>().toList();
-        draftList.sort((a, b) => b.lastModified.compareTo(a.lastModified));
-        return draftList;
-      } else {
-        final draftList = drafts.values.cast<ComposeDraftModel>().toList();
-        draftList.sort((a, b) => b.lastModified.compareTo(a.lastModified));
-        return draftList;
-      }
+    // Track loading state based on drafts being loaded
+    useEffect(() {
+      // Set loading to false after drafts are loaded
+      // We consider drafts loaded when the provider has been initialized
+      Future.microtask(() {
+        if (isLoading.value) {
+          isLoading.value = false;
+        }
+      });
+      return null;
     }, [drafts]);
 
+    final sortedDrafts = useMemoized(
+      () {
+        final draftList = drafts.values.toList();
+        draftList.sort((a, b) => b.updatedAt!.compareTo(a.updatedAt!));
+        return draftList;
+      },
+      [
+        drafts.length,
+        drafts.values.map((e) => e.updatedAt!.millisecondsSinceEpoch).join(),
+      ],
+    );
+
     return Scaffold(
-      appBar: AppBar(
-        title: Text(isArticle ? 'articleDrafts'.tr() : 'postDrafts'.tr()),
-      ),
-      body: Column(
-        children: [
-          if (sortedDrafts.isEmpty)
-            Expanded(
-              child: Center(
-                child: Column(
-                  mainAxisAlignment: MainAxisAlignment.center,
-                  children: [
-                    Icon(
-                      Symbols.draft,
-                      size: 64,
-                      color: colorScheme.onSurface.withOpacity(0.3),
+      appBar: AppBar(title: Text('drafts'.tr())),
+      body:
+          isLoading.value
+              ? const Center(child: CircularProgressIndicator())
+              : Column(
+                children: [
+                  if (sortedDrafts.isEmpty)
+                    Expanded(
+                      child: Center(
+                        child: Column(
+                          mainAxisAlignment: MainAxisAlignment.center,
+                          children: [
+                            Icon(
+                              Symbols.draft,
+                              size: 64,
+                              color: colorScheme.onSurface.withOpacity(0.3),
+                            ),
+                            const Gap(16),
+                            Text(
+                              'noDrafts'.tr(),
+                              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);
+                            },
+                          );
+                        },
+                      ),
                     ),
-                    const Gap(16),
-                    Text(
-                      'noDrafts'.tr(),
-                      style: theme.textTheme.bodyLarge?.copyWith(
-                        color: colorScheme.onSurface.withOpacity(0.6),
+                  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) {
+                                  await ref
+                                      .read(
+                                        composeStorageNotifierProvider.notifier,
+                                      )
+                                      .clearAllDrafts();
+                                }
+                              },
+                              icon: const Icon(Symbols.delete_sweep),
+                              label: Text('clearAll'.tr()),
+                            ),
+                          ),
+                        ],
                       ),
                     ),
                   ],
-                ),
-              ),
-            )
-          else
-            Expanded(
-              child: ListView.builder(
-                itemCount: sortedDrafts.length,
-                itemBuilder: (context, index) {
-                  final draft = sortedDrafts[index];
-                  return _DraftItem(
-                    draft: draft,
-                    isArticle: isArticle,
-                    onTap: () {
-                      Navigator.of(context).pop();
-                      final draftId =
-                          isArticle
-                              ? (draft as ArticleDraftModel).id
-                              : (draft as ComposeDraftModel).id;
-                      onDraftSelected?.call(draftId);
-                    },
-                    onDelete: () async {
-                      final draftId =
-                          isArticle
-                              ? (draft as ArticleDraftModel).id
-                              : (draft as ComposeDraftModel).id;
-                      if (isArticle) {
-                        await ref
-                            .read(articleStorageNotifierProvider.notifier)
-                            .deleteDraft(draftId);
-                      } else {
-                        await ref
-                            .read(composeStorageNotifierProvider.notifier)
-                            .deleteDraft(draftId);
-                      }
-                    },
-                  );
-                },
-              ),
-            ),
-          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) {
-                          if (isArticle) {
-                            await ref
-                                .read(articleStorageNotifierProvider.notifier)
-                                .clearAllDrafts();
-                          } else {
-                            await ref
-                                .read(composeStorageNotifierProvider.notifier)
-                                .clearAllDrafts();
-                          }
-                        }
-                      },
-                      icon: const Icon(Symbols.delete_sweep),
-                      label: Text('clearAll'.tr()),
-                    ),
-                  ),
                 ],
               ),
-            ),
-          ],
-        ],
-      ),
     );
   }
 }
 
 class _DraftItem extends StatelessWidget {
-  final dynamic draft; // ComposeDraft or ArticleDraft
-  final bool isArticle;
+  final dynamic draft;
   final VoidCallback? onTap;
   final VoidCallback? onDelete;
 
-  const _DraftItem({
-    required this.draft,
-    required this.isArticle,
-    this.onTap,
-    this.onDelete,
-  });
+  const _DraftItem({required this.draft, this.onTap, this.onDelete});
 
   @override
   Widget build(BuildContext context) {
     final theme = Theme.of(context);
     final colorScheme = theme.colorScheme;
 
-    final String title;
-    final String content;
-    final DateTime lastModified;
-    final String visibility;
-
-    if (isArticle) {
-      final articleDraft = draft as ArticleDraftModel;
-      title =
-          articleDraft.title.isNotEmpty ? articleDraft.title : 'untitled'.tr();
-      content =
-          articleDraft.content.isNotEmpty
-              ? articleDraft.content
-              : (articleDraft.description.isNotEmpty
-                  ? articleDraft.description
-                  : 'noContent'.tr());
-      lastModified = articleDraft.lastModified;
-      visibility = _parseArticleVisibility(articleDraft.visibility);
-    } else {
-      final postDraft = draft as ComposeDraftModel;
-      title = postDraft.title.isNotEmpty ? postDraft.title : 'untitled'.tr();
-      content =
-          postDraft.content.isNotEmpty
-              ? postDraft.content
-              : (postDraft.description.isNotEmpty
-                  ? postDraft.description
-                  : 'noContent'.tr());
-      lastModified = postDraft.lastModified;
-      visibility = _parseArticleVisibility(postDraft.visibility);
-    }
-
+    final title = draft.title ?? 'untitled'.tr();
+    final content = draft.content ?? (draft.description ?? 'noContent'.tr());
     final preview =
         content.length > 100 ? '${content.substring(0, 100)}...' : content;
-    final timeAgo = _formatTimeAgo(lastModified);
+    final timeAgo = _formatTimeAgo(draft.updatedAt!);
+    final visibility = _parseVisibility(draft.visibility);
 
     return Card(
       margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
@@ -223,7 +183,7 @@ class _DraftItem extends StatelessWidget {
               Row(
                 children: [
                   Icon(
-                    isArticle ? Symbols.article : Symbols.post_add,
+                    draft.type == 1 ? Symbols.article : Symbols.post_add,
                     size: 20,
                     color: colorScheme.primary,
                   ),
@@ -316,7 +276,7 @@ class _DraftItem extends StatelessWidget {
     }
   }
 
-  String _parseArticleVisibility(int visibility) {
+  String _parseVisibility(int visibility) {
     switch (visibility) {
       case 0:
         return 'public'.tr();
diff --git a/lib/widgets/post/post_item.dart b/lib/widgets/post/post_item.dart
index d52c6f4..9732c05 100644
--- a/lib/widgets/post/post_item.dart
+++ b/lib/widgets/post/post_item.dart
@@ -162,8 +162,8 @@ class PostItem extends HookConsumerWidget {
                               Spacer(),
                               Text(
                                 isFullPost
-                                    ? item.publishedAt.formatSystem()
-                                    : item.publishedAt.formatRelative(context),
+                                    ? item.publishedAt?.formatSystem() ?? ''
+                                    : item.publishedAt?.formatRelative(context) ?? '',
                               ).fontSize(11).alignment(Alignment.bottomRight),
                               const Gap(4),
                             ],
@@ -213,12 +213,14 @@ class PostItem extends HookConsumerWidget {
                               content: item.content!,
                               linesMargin:
                                   item.type == 0
-                                      ? EdgeInsets.only(bottom: 4)
+                                      ? EdgeInsets.only(bottom: 8)
                                       : null,
                             ),
                           // Show truncation hint if post is truncated
                           if (item.isTruncated && !isFullPost)
-                            _PostTruncateHint(),
+                            _PostTruncateHint().padding(
+                              bottom: item.attachments.isNotEmpty ? 8 : null,
+                            ),
                           if ((item.repliedPost != null ||
                                   item.forwardedPost != null) &&
                               showReferencePost)
@@ -234,7 +236,7 @@ class PostItem extends HookConsumerWidget {
                                 MediaQuery.of(context).size.width * 0.9,
                                 kWideScreenWidth - 160,
                               ),
-                            ).padding(top: 4),
+                            ),
                           // Render embed links
                           if (item.meta?['embeds'] != null)
                             ...((item.meta!['embeds'] as List<dynamic>)
@@ -248,7 +250,8 @@ class PostItem extends HookConsumerWidget {
                                       MediaQuery.of(context).size.width * 0.85,
                                       kWideScreenWidth - 160,
                                     ),
-                                  ).padding(top: 4),
+                                    margin: EdgeInsets.only(top: 8),
+                                  ),
                                 )),
                         ],
                       ),
@@ -323,7 +326,6 @@ Widget _buildReferencePost(BuildContext context, SnPost item) {
   final isReply = item.repliedPost != null;
 
   return Container(
-    margin: const EdgeInsets.only(top: 8, bottom: 8),
     padding: const EdgeInsets.all(12),
     decoration: BoxDecoration(
       color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5),
diff --git a/lib/widgets/post/post_item_creator.dart b/lib/widgets/post/post_item_creator.dart
index d27cf48..380cfd2 100644
--- a/lib/widgets/post/post_item_creator.dart
+++ b/lib/widgets/post/post_item_creator.dart
@@ -153,7 +153,7 @@ class PostItemCreator extends HookConsumerWidget {
             ),
             const Gap(8),
             Text(
-              item.publishedAt.formatSystem(),
+              item.publishedAt?.formatSystem() ?? '',
               style: TextStyle(
                 fontSize: 12,
                 color: Theme.of(context).colorScheme.secondary,
@@ -291,7 +291,7 @@ class PostItemCreator extends HookConsumerWidget {
           mainAxisAlignment: MainAxisAlignment.spaceBetween,
           children: [
             Text(
-              'Created: ${item.createdAt.formatSystem()}',
+              'Created: ${item.createdAt?.formatSystem() ?? ''}',
               style: TextStyle(
                 fontSize: 12,
                 color: Theme.of(context).colorScheme.secondary,