diff --git a/assets/translations/en-US.json b/assets/translations/en-US.json
index 1f3ac03..3d33963 100644
--- a/assets/translations/en-US.json
+++ b/assets/translations/en-US.json
@@ -866,5 +866,9 @@
     "other": "Maximum ask for {} steps authenticate"
   },
   "authAlwaysRisky": "Always Risky",
-  "authAlwaysRiskyDescription": "Always ask for the highest steps count of authentication when logging in."
+  "authAlwaysRiskyDescription": "Always ask for the highest steps count of authentication when logging in.",
+  "chatUnjoined": "Unjoined Channel",
+  "chatUnjoinedDescription": "You haven't joined this channel, so you can't send messages either view messages in it.",
+  "chatUnjoinedPublicDescription": "Fortunately, this is a public channel, so you can join it as you want.",
+  "chatJoin": "Join the Channel"
 }
diff --git a/assets/translations/zh-CN.json b/assets/translations/zh-CN.json
index 1212020..e4d3fa8 100644
--- a/assets/translations/zh-CN.json
+++ b/assets/translations/zh-CN.json
@@ -864,5 +864,9 @@
     "other": "登入时最多要求 {} 步验证"
   },
   "authAlwaysRisky": "总是风险",
-  "authAlwaysRiskyDescription": "在登入时始终按最高标准要求验证。"
+  "authAlwaysRiskyDescription": "在登入时始终按最高标准要求验证。",
+  "chatUnjoined": "未加入频道",
+  "chatUnjoinedDescription": "你没有加入这个频道,所以你也无法发送消息或者查看这个频道中的消息。",
+  "chatUnjoinedPublicDescription": "但幸运的是,这是一个公开频道,所以你可以主动加入。",
+  "chatJoin": "加入频道"
 }
diff --git a/drift_schemas/my_database/drift_schema_v4.json b/drift_schemas/my_database/drift_schema_v4.json
new file mode 100644
index 0000000..cab3643
--- /dev/null
+++ b/drift_schemas/my_database/drift_schema_v4.json
@@ -0,0 +1 @@
+{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"sn_local_chat_channel","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"alias","getter_name":"alias","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"content","getter_name":"content","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const SnChannelConverter()","dart_type_name":"SnChannel"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":1,"references":[],"type":"table","data":{"name":"sn_local_chat_message","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"channel_id","getter_name":"channelId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"sender_id","getter_name":"senderId","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"content","getter_name":"content","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const SnMessageConverter()","dart_type_name":"SnChatMessage"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":2,"references":[],"type":"table","data":{"name":"sn_local_channel_member","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"channel_id","getter_name":"channelId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"account_id","getter_name":"accountId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"content","getter_name":"content","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"SnChannelMemberConverter()","dart_type_name":"SnChannelMember"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]},{"name":"cache_expired_at","getter_name":"cacheExpiredAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":3,"references":[],"type":"table","data":{"name":"sn_local_key_pair","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"account_id","getter_name":"accountId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"public_key","getter_name":"publicKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"private_key","getter_name":"privateKey","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_active","getter_name":"isActive","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_active\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_active\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"sn_local_account","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"content","getter_name":"content","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const SnAccountConverter()","dart_type_name":"SnAccount"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]},{"name":"cache_expired_at","getter_name":"cacheExpiredAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":5,"references":[],"type":"table","data":{"name":"sn_local_attachment","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"rid","getter_name":"rid","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"UNIQUE","dialectAwareDefaultConstraints":{"sqlite":"UNIQUE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"uuid","getter_name":"uuid","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"UNIQUE","dialectAwareDefaultConstraints":{"sqlite":"UNIQUE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"content","getter_name":"content","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const SnAttachmentConverter()","dart_type_name":"SnAttachment"}},{"name":"account_id","getter_name":"accountId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]},{"name":"cache_expired_at","getter_name":"cacheExpiredAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":6,"references":[],"type":"table","data":{"name":"sn_local_sticker","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"alias","getter_name":"alias","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"full_alias","getter_name":"fullAlias","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"content","getter_name":"content","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const SnStickerConverter()","dart_type_name":"SnSticker"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":7,"references":[],"type":"table","data":{"name":"sn_local_sticker_pack","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"content","getter_name":"content","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const SnStickerPackConverter()","dart_type_name":"SnStickerPack"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":8,"references":[],"type":"table","data":{"name":"sn_local_realm","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","dialectAwareDefaultConstraints":{"sqlite":"PRIMARY KEY AUTOINCREMENT"},"default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"alias","getter_name":"alias","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"UNIQUE","dialectAwareDefaultConstraints":{"sqlite":"UNIQUE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"content","getter_name":"content","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const SnRealmConverter()","dart_type_name":"SnRealm"}},{"name":"account_id","getter_name":"accountId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CAST(strftime(\\'%s\\', CURRENT_TIMESTAMP) AS INTEGER)')","default_client_dart":null,"dsl_features":[]},{"name":"cache_expired_at","getter_name":"cacheExpiredAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":9,"references":[0],"type":"index","data":{"on":0,"name":"idx_channel_alias","sql":null,"unique":false,"columns":["alias"]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"idx_chat_channel","sql":null,"unique":false,"columns":["channel_id"]}},{"id":11,"references":[4],"type":"index","data":{"on":4,"name":"idx_account_name","sql":null,"unique":false,"columns":["name"]}},{"id":12,"references":[5],"type":"index","data":{"on":5,"name":"idx_attachment_rid","sql":null,"unique":false,"columns":["rid"]}},{"id":13,"references":[5],"type":"index","data":{"on":5,"name":"idx_attachment_account","sql":null,"unique":false,"columns":["account_id"]}},{"id":14,"references":[8],"type":"index","data":{"on":8,"name":"idx_realm_alias","sql":null,"unique":false,"columns":["alias"]}},{"id":15,"references":[8],"type":"index","data":{"on":8,"name":"idx_realm_account","sql":null,"unique":false,"columns":["account_id"]}}]}
\ No newline at end of file
diff --git a/lib/database/database.dart b/lib/database/database.dart
index 6c8e091..dd3af82 100644
--- a/lib/database/database.dart
+++ b/lib/database/database.dart
@@ -6,10 +6,12 @@ import 'package:surface/database/attachment.dart';
 import 'package:surface/database/chat.dart';
 import 'package:surface/database/database.steps.dart';
 import 'package:surface/database/keypair.dart';
+import 'package:surface/database/realm.dart';
 import 'package:surface/database/sticker.dart';
 import 'package:surface/types/chat.dart';
 import 'package:surface/types/attachment.dart';
 import 'package:surface/types/account.dart';
+import 'package:surface/types/realm.dart';
 
 part 'database.g.dart';
 
@@ -22,12 +24,13 @@ part 'database.g.dart';
   SnLocalAttachment,
   SnLocalSticker,
   SnLocalStickerPack,
+  SnLocalRealm,
 ])
 class AppDatabase extends _$AppDatabase {
   AppDatabase([QueryExecutor? e]) : super(e ?? _openConnection());
 
   @override
-  int get schemaVersion => 3;
+  int get schemaVersion => 4;
 
   static QueryExecutor _openConnection() {
     return driftDatabase(
@@ -49,6 +52,10 @@ class AppDatabase extends _$AppDatabase {
         // Nothing else to do here
       }, from2To3: (m, schema) async {
         // Nothing else to do here, too
+      }, from3To4: (m, schema) async {
+        m.createTable(schema.snLocalRealm);
+        m.createIndex(schema.idxRealmAccount);
+        m.createIndex(schema.idxRealmAlias);
       }),
     );
   }
diff --git a/lib/database/database.g.dart b/lib/database/database.g.dart
index 796bcff..a0193aa 100644
--- a/lib/database/database.g.dart
+++ b/lib/database/database.g.dart
@@ -2454,6 +2454,351 @@ class SnLocalStickerPackCompanion
   }
 }
 
+class $SnLocalRealmTable extends SnLocalRealm
+    with TableInfo<$SnLocalRealmTable, SnLocalRealmData> {
+  @override
+  final GeneratedDatabase attachedDatabase;
+  final String? _alias;
+  $SnLocalRealmTable(this.attachedDatabase, [this._alias]);
+  static const VerificationMeta _idMeta = const VerificationMeta('id');
+  @override
+  late final GeneratedColumn<int> id = GeneratedColumn<int>(
+      'id', aliasedName, false,
+      hasAutoIncrement: true,
+      type: DriftSqlType.int,
+      requiredDuringInsert: false,
+      defaultConstraints:
+          GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
+  static const VerificationMeta _aliasMeta = const VerificationMeta('alias');
+  @override
+  late final GeneratedColumn<String> alias = GeneratedColumn<String>(
+      'alias', aliasedName, false,
+      type: DriftSqlType.string,
+      requiredDuringInsert: true,
+      defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE'));
+  @override
+  late final GeneratedColumnWithTypeConverter<SnRealm, String> content =
+      GeneratedColumn<String>('content', aliasedName, false,
+              type: DriftSqlType.string, requiredDuringInsert: true)
+          .withConverter<SnRealm>($SnLocalRealmTable.$convertercontent);
+  static const VerificationMeta _accountIdMeta =
+      const VerificationMeta('accountId');
+  @override
+  late final GeneratedColumn<int> accountId = GeneratedColumn<int>(
+      'account_id', aliasedName, false,
+      type: DriftSqlType.int, requiredDuringInsert: true);
+  static const VerificationMeta _createdAtMeta =
+      const VerificationMeta('createdAt');
+  @override
+  late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
+      'created_at', aliasedName, false,
+      type: DriftSqlType.dateTime,
+      requiredDuringInsert: false,
+      defaultValue: currentDateAndTime);
+  static const VerificationMeta _cacheExpiredAtMeta =
+      const VerificationMeta('cacheExpiredAt');
+  @override
+  late final GeneratedColumn<DateTime> cacheExpiredAt =
+      GeneratedColumn<DateTime>('cache_expired_at', aliasedName, false,
+          type: DriftSqlType.dateTime, requiredDuringInsert: true);
+  @override
+  List<GeneratedColumn> get $columns =>
+      [id, alias, content, accountId, createdAt, cacheExpiredAt];
+  @override
+  String get aliasedName => _alias ?? actualTableName;
+  @override
+  String get actualTableName => $name;
+  static const String $name = 'sn_local_realm';
+  @override
+  VerificationContext validateIntegrity(Insertable<SnLocalRealmData> instance,
+      {bool isInserting = false}) {
+    final context = VerificationContext();
+    final data = instance.toColumns(true);
+    if (data.containsKey('id')) {
+      context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
+    }
+    if (data.containsKey('alias')) {
+      context.handle(
+          _aliasMeta, alias.isAcceptableOrUnknown(data['alias']!, _aliasMeta));
+    } else if (isInserting) {
+      context.missing(_aliasMeta);
+    }
+    if (data.containsKey('account_id')) {
+      context.handle(_accountIdMeta,
+          accountId.isAcceptableOrUnknown(data['account_id']!, _accountIdMeta));
+    } else if (isInserting) {
+      context.missing(_accountIdMeta);
+    }
+    if (data.containsKey('created_at')) {
+      context.handle(_createdAtMeta,
+          createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
+    }
+    if (data.containsKey('cache_expired_at')) {
+      context.handle(
+          _cacheExpiredAtMeta,
+          cacheExpiredAt.isAcceptableOrUnknown(
+              data['cache_expired_at']!, _cacheExpiredAtMeta));
+    } else if (isInserting) {
+      context.missing(_cacheExpiredAtMeta);
+    }
+    return context;
+  }
+
+  @override
+  Set<GeneratedColumn> get $primaryKey => {id};
+  @override
+  SnLocalRealmData map(Map<String, dynamic> data, {String? tablePrefix}) {
+    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+    return SnLocalRealmData(
+      id: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}id'])!,
+      alias: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}alias'])!,
+      content: $SnLocalRealmTable.$convertercontent.fromSql(attachedDatabase
+          .typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}content'])!),
+      accountId: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}account_id'])!,
+      createdAt: attachedDatabase.typeMapping
+          .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
+      cacheExpiredAt: attachedDatabase.typeMapping.read(
+          DriftSqlType.dateTime, data['${effectivePrefix}cache_expired_at'])!,
+    );
+  }
+
+  @override
+  $SnLocalRealmTable createAlias(String alias) {
+    return $SnLocalRealmTable(attachedDatabase, alias);
+  }
+
+  static JsonTypeConverter2<SnRealm, String, Map<String, Object?>>
+      $convertercontent = const SnRealmConverter();
+}
+
+class SnLocalRealmData extends DataClass
+    implements Insertable<SnLocalRealmData> {
+  final int id;
+  final String alias;
+  final SnRealm content;
+  final int accountId;
+  final DateTime createdAt;
+  final DateTime cacheExpiredAt;
+  const SnLocalRealmData(
+      {required this.id,
+      required this.alias,
+      required this.content,
+      required this.accountId,
+      required this.createdAt,
+      required this.cacheExpiredAt});
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    map['id'] = Variable<int>(id);
+    map['alias'] = Variable<String>(alias);
+    {
+      map['content'] =
+          Variable<String>($SnLocalRealmTable.$convertercontent.toSql(content));
+    }
+    map['account_id'] = Variable<int>(accountId);
+    map['created_at'] = Variable<DateTime>(createdAt);
+    map['cache_expired_at'] = Variable<DateTime>(cacheExpiredAt);
+    return map;
+  }
+
+  SnLocalRealmCompanion toCompanion(bool nullToAbsent) {
+    return SnLocalRealmCompanion(
+      id: Value(id),
+      alias: Value(alias),
+      content: Value(content),
+      accountId: Value(accountId),
+      createdAt: Value(createdAt),
+      cacheExpiredAt: Value(cacheExpiredAt),
+    );
+  }
+
+  factory SnLocalRealmData.fromJson(Map<String, dynamic> json,
+      {ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return SnLocalRealmData(
+      id: serializer.fromJson<int>(json['id']),
+      alias: serializer.fromJson<String>(json['alias']),
+      content: $SnLocalRealmTable.$convertercontent
+          .fromJson(serializer.fromJson<Map<String, Object?>>(json['content'])),
+      accountId: serializer.fromJson<int>(json['accountId']),
+      createdAt: serializer.fromJson<DateTime>(json['createdAt']),
+      cacheExpiredAt: serializer.fromJson<DateTime>(json['cacheExpiredAt']),
+    );
+  }
+  @override
+  Map<String, dynamic> toJson({ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return <String, dynamic>{
+      'id': serializer.toJson<int>(id),
+      'alias': serializer.toJson<String>(alias),
+      'content': serializer.toJson<Map<String, Object?>>(
+          $SnLocalRealmTable.$convertercontent.toJson(content)),
+      'accountId': serializer.toJson<int>(accountId),
+      'createdAt': serializer.toJson<DateTime>(createdAt),
+      'cacheExpiredAt': serializer.toJson<DateTime>(cacheExpiredAt),
+    };
+  }
+
+  SnLocalRealmData copyWith(
+          {int? id,
+          String? alias,
+          SnRealm? content,
+          int? accountId,
+          DateTime? createdAt,
+          DateTime? cacheExpiredAt}) =>
+      SnLocalRealmData(
+        id: id ?? this.id,
+        alias: alias ?? this.alias,
+        content: content ?? this.content,
+        accountId: accountId ?? this.accountId,
+        createdAt: createdAt ?? this.createdAt,
+        cacheExpiredAt: cacheExpiredAt ?? this.cacheExpiredAt,
+      );
+  SnLocalRealmData copyWithCompanion(SnLocalRealmCompanion data) {
+    return SnLocalRealmData(
+      id: data.id.present ? data.id.value : this.id,
+      alias: data.alias.present ? data.alias.value : this.alias,
+      content: data.content.present ? data.content.value : this.content,
+      accountId: data.accountId.present ? data.accountId.value : this.accountId,
+      createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
+      cacheExpiredAt: data.cacheExpiredAt.present
+          ? data.cacheExpiredAt.value
+          : this.cacheExpiredAt,
+    );
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalRealmData(')
+          ..write('id: $id, ')
+          ..write('alias: $alias, ')
+          ..write('content: $content, ')
+          ..write('accountId: $accountId, ')
+          ..write('createdAt: $createdAt, ')
+          ..write('cacheExpiredAt: $cacheExpiredAt')
+          ..write(')'))
+        .toString();
+  }
+
+  @override
+  int get hashCode =>
+      Object.hash(id, alias, content, accountId, createdAt, cacheExpiredAt);
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      (other is SnLocalRealmData &&
+          other.id == this.id &&
+          other.alias == this.alias &&
+          other.content == this.content &&
+          other.accountId == this.accountId &&
+          other.createdAt == this.createdAt &&
+          other.cacheExpiredAt == this.cacheExpiredAt);
+}
+
+class SnLocalRealmCompanion extends UpdateCompanion<SnLocalRealmData> {
+  final Value<int> id;
+  final Value<String> alias;
+  final Value<SnRealm> content;
+  final Value<int> accountId;
+  final Value<DateTime> createdAt;
+  final Value<DateTime> cacheExpiredAt;
+  const SnLocalRealmCompanion({
+    this.id = const Value.absent(),
+    this.alias = const Value.absent(),
+    this.content = const Value.absent(),
+    this.accountId = const Value.absent(),
+    this.createdAt = const Value.absent(),
+    this.cacheExpiredAt = const Value.absent(),
+  });
+  SnLocalRealmCompanion.insert({
+    this.id = const Value.absent(),
+    required String alias,
+    required SnRealm content,
+    required int accountId,
+    this.createdAt = const Value.absent(),
+    required DateTime cacheExpiredAt,
+  })  : alias = Value(alias),
+        content = Value(content),
+        accountId = Value(accountId),
+        cacheExpiredAt = Value(cacheExpiredAt);
+  static Insertable<SnLocalRealmData> custom({
+    Expression<int>? id,
+    Expression<String>? alias,
+    Expression<String>? content,
+    Expression<int>? accountId,
+    Expression<DateTime>? createdAt,
+    Expression<DateTime>? cacheExpiredAt,
+  }) {
+    return RawValuesInsertable({
+      if (id != null) 'id': id,
+      if (alias != null) 'alias': alias,
+      if (content != null) 'content': content,
+      if (accountId != null) 'account_id': accountId,
+      if (createdAt != null) 'created_at': createdAt,
+      if (cacheExpiredAt != null) 'cache_expired_at': cacheExpiredAt,
+    });
+  }
+
+  SnLocalRealmCompanion copyWith(
+      {Value<int>? id,
+      Value<String>? alias,
+      Value<SnRealm>? content,
+      Value<int>? accountId,
+      Value<DateTime>? createdAt,
+      Value<DateTime>? cacheExpiredAt}) {
+    return SnLocalRealmCompanion(
+      id: id ?? this.id,
+      alias: alias ?? this.alias,
+      content: content ?? this.content,
+      accountId: accountId ?? this.accountId,
+      createdAt: createdAt ?? this.createdAt,
+      cacheExpiredAt: cacheExpiredAt ?? this.cacheExpiredAt,
+    );
+  }
+
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    if (id.present) {
+      map['id'] = Variable<int>(id.value);
+    }
+    if (alias.present) {
+      map['alias'] = Variable<String>(alias.value);
+    }
+    if (content.present) {
+      map['content'] = Variable<String>(
+          $SnLocalRealmTable.$convertercontent.toSql(content.value));
+    }
+    if (accountId.present) {
+      map['account_id'] = Variable<int>(accountId.value);
+    }
+    if (createdAt.present) {
+      map['created_at'] = Variable<DateTime>(createdAt.value);
+    }
+    if (cacheExpiredAt.present) {
+      map['cache_expired_at'] = Variable<DateTime>(cacheExpiredAt.value);
+    }
+    return map;
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalRealmCompanion(')
+          ..write('id: $id, ')
+          ..write('alias: $alias, ')
+          ..write('content: $content, ')
+          ..write('accountId: $accountId, ')
+          ..write('createdAt: $createdAt, ')
+          ..write('cacheExpiredAt: $cacheExpiredAt')
+          ..write(')'))
+        .toString();
+  }
+}
+
 abstract class _$AppDatabase extends GeneratedDatabase {
   _$AppDatabase(QueryExecutor e) : super(e);
   $AppDatabaseManager get managers => $AppDatabaseManager(this);
@@ -2470,6 +2815,7 @@ abstract class _$AppDatabase extends GeneratedDatabase {
   late final $SnLocalStickerTable snLocalSticker = $SnLocalStickerTable(this);
   late final $SnLocalStickerPackTable snLocalStickerPack =
       $SnLocalStickerPackTable(this);
+  late final $SnLocalRealmTable snLocalRealm = $SnLocalRealmTable(this);
   late final Index idxChannelAlias = Index('idx_channel_alias',
       'CREATE INDEX idx_channel_alias ON sn_local_chat_channel (alias)');
   late final Index idxChatChannel = Index('idx_chat_channel',
@@ -2480,6 +2826,10 @@ abstract class _$AppDatabase extends GeneratedDatabase {
       'CREATE INDEX idx_attachment_rid ON sn_local_attachment (rid)');
   late final Index idxAttachmentAccount = Index('idx_attachment_account',
       'CREATE INDEX idx_attachment_account ON sn_local_attachment (account_id)');
+  late final Index idxRealmAlias = Index('idx_realm_alias',
+      'CREATE INDEX idx_realm_alias ON sn_local_realm (alias)');
+  late final Index idxRealmAccount = Index('idx_realm_account',
+      'CREATE INDEX idx_realm_account ON sn_local_realm (account_id)');
   @override
   Iterable<TableInfo<Table, Object?>> get allTables =>
       allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@@ -2493,11 +2843,14 @@ abstract class _$AppDatabase extends GeneratedDatabase {
         snLocalAttachment,
         snLocalSticker,
         snLocalStickerPack,
+        snLocalRealm,
         idxChannelAlias,
         idxChatChannel,
         idxAccountName,
         idxAttachmentRid,
-        idxAttachmentAccount
+        idxAttachmentAccount,
+        idxRealmAlias,
+        idxRealmAccount
       ];
 }
 
@@ -3888,6 +4241,192 @@ typedef $$SnLocalStickerPackTableProcessedTableManager = ProcessedTableManager<
     ),
     SnLocalStickerPackData,
     PrefetchHooks Function()>;
+typedef $$SnLocalRealmTableCreateCompanionBuilder = SnLocalRealmCompanion
+    Function({
+  Value<int> id,
+  required String alias,
+  required SnRealm content,
+  required int accountId,
+  Value<DateTime> createdAt,
+  required DateTime cacheExpiredAt,
+});
+typedef $$SnLocalRealmTableUpdateCompanionBuilder = SnLocalRealmCompanion
+    Function({
+  Value<int> id,
+  Value<String> alias,
+  Value<SnRealm> content,
+  Value<int> accountId,
+  Value<DateTime> createdAt,
+  Value<DateTime> cacheExpiredAt,
+});
+
+class $$SnLocalRealmTableFilterComposer
+    extends Composer<_$AppDatabase, $SnLocalRealmTable> {
+  $$SnLocalRealmTableFilterComposer({
+    required super.$db,
+    required super.$table,
+    super.joinBuilder,
+    super.$addJoinBuilderToRootComposer,
+    super.$removeJoinBuilderFromRootComposer,
+  });
+  ColumnFilters<int> get id => $composableBuilder(
+      column: $table.id, builder: (column) => ColumnFilters(column));
+
+  ColumnFilters<String> get alias => $composableBuilder(
+      column: $table.alias, builder: (column) => ColumnFilters(column));
+
+  ColumnWithTypeConverterFilters<SnRealm, SnRealm, String> get content =>
+      $composableBuilder(
+          column: $table.content,
+          builder: (column) => ColumnWithTypeConverterFilters(column));
+
+  ColumnFilters<int> get accountId => $composableBuilder(
+      column: $table.accountId, builder: (column) => ColumnFilters(column));
+
+  ColumnFilters<DateTime> get createdAt => $composableBuilder(
+      column: $table.createdAt, builder: (column) => ColumnFilters(column));
+
+  ColumnFilters<DateTime> get cacheExpiredAt => $composableBuilder(
+      column: $table.cacheExpiredAt,
+      builder: (column) => ColumnFilters(column));
+}
+
+class $$SnLocalRealmTableOrderingComposer
+    extends Composer<_$AppDatabase, $SnLocalRealmTable> {
+  $$SnLocalRealmTableOrderingComposer({
+    required super.$db,
+    required super.$table,
+    super.joinBuilder,
+    super.$addJoinBuilderToRootComposer,
+    super.$removeJoinBuilderFromRootComposer,
+  });
+  ColumnOrderings<int> get id => $composableBuilder(
+      column: $table.id, builder: (column) => ColumnOrderings(column));
+
+  ColumnOrderings<String> get alias => $composableBuilder(
+      column: $table.alias, builder: (column) => ColumnOrderings(column));
+
+  ColumnOrderings<String> get content => $composableBuilder(
+      column: $table.content, builder: (column) => ColumnOrderings(column));
+
+  ColumnOrderings<int> get accountId => $composableBuilder(
+      column: $table.accountId, builder: (column) => ColumnOrderings(column));
+
+  ColumnOrderings<DateTime> get createdAt => $composableBuilder(
+      column: $table.createdAt, builder: (column) => ColumnOrderings(column));
+
+  ColumnOrderings<DateTime> get cacheExpiredAt => $composableBuilder(
+      column: $table.cacheExpiredAt,
+      builder: (column) => ColumnOrderings(column));
+}
+
+class $$SnLocalRealmTableAnnotationComposer
+    extends Composer<_$AppDatabase, $SnLocalRealmTable> {
+  $$SnLocalRealmTableAnnotationComposer({
+    required super.$db,
+    required super.$table,
+    super.joinBuilder,
+    super.$addJoinBuilderToRootComposer,
+    super.$removeJoinBuilderFromRootComposer,
+  });
+  GeneratedColumn<int> get id =>
+      $composableBuilder(column: $table.id, builder: (column) => column);
+
+  GeneratedColumn<String> get alias =>
+      $composableBuilder(column: $table.alias, builder: (column) => column);
+
+  GeneratedColumnWithTypeConverter<SnRealm, String> get content =>
+      $composableBuilder(column: $table.content, builder: (column) => column);
+
+  GeneratedColumn<int> get accountId =>
+      $composableBuilder(column: $table.accountId, builder: (column) => column);
+
+  GeneratedColumn<DateTime> get createdAt =>
+      $composableBuilder(column: $table.createdAt, builder: (column) => column);
+
+  GeneratedColumn<DateTime> get cacheExpiredAt => $composableBuilder(
+      column: $table.cacheExpiredAt, builder: (column) => column);
+}
+
+class $$SnLocalRealmTableTableManager extends RootTableManager<
+    _$AppDatabase,
+    $SnLocalRealmTable,
+    SnLocalRealmData,
+    $$SnLocalRealmTableFilterComposer,
+    $$SnLocalRealmTableOrderingComposer,
+    $$SnLocalRealmTableAnnotationComposer,
+    $$SnLocalRealmTableCreateCompanionBuilder,
+    $$SnLocalRealmTableUpdateCompanionBuilder,
+    (
+      SnLocalRealmData,
+      BaseReferences<_$AppDatabase, $SnLocalRealmTable, SnLocalRealmData>
+    ),
+    SnLocalRealmData,
+    PrefetchHooks Function()> {
+  $$SnLocalRealmTableTableManager(_$AppDatabase db, $SnLocalRealmTable table)
+      : super(TableManagerState(
+          db: db,
+          table: table,
+          createFilteringComposer: () =>
+              $$SnLocalRealmTableFilterComposer($db: db, $table: table),
+          createOrderingComposer: () =>
+              $$SnLocalRealmTableOrderingComposer($db: db, $table: table),
+          createComputedFieldComposer: () =>
+              $$SnLocalRealmTableAnnotationComposer($db: db, $table: table),
+          updateCompanionCallback: ({
+            Value<int> id = const Value.absent(),
+            Value<String> alias = const Value.absent(),
+            Value<SnRealm> content = const Value.absent(),
+            Value<int> accountId = const Value.absent(),
+            Value<DateTime> createdAt = const Value.absent(),
+            Value<DateTime> cacheExpiredAt = const Value.absent(),
+          }) =>
+              SnLocalRealmCompanion(
+            id: id,
+            alias: alias,
+            content: content,
+            accountId: accountId,
+            createdAt: createdAt,
+            cacheExpiredAt: cacheExpiredAt,
+          ),
+          createCompanionCallback: ({
+            Value<int> id = const Value.absent(),
+            required String alias,
+            required SnRealm content,
+            required int accountId,
+            Value<DateTime> createdAt = const Value.absent(),
+            required DateTime cacheExpiredAt,
+          }) =>
+              SnLocalRealmCompanion.insert(
+            id: id,
+            alias: alias,
+            content: content,
+            accountId: accountId,
+            createdAt: createdAt,
+            cacheExpiredAt: cacheExpiredAt,
+          ),
+          withReferenceMapper: (p0) => p0
+              .map((e) => (e.readTable(table), BaseReferences(db, table, e)))
+              .toList(),
+          prefetchHooksCallback: null,
+        ));
+}
+
+typedef $$SnLocalRealmTableProcessedTableManager = ProcessedTableManager<
+    _$AppDatabase,
+    $SnLocalRealmTable,
+    SnLocalRealmData,
+    $$SnLocalRealmTableFilterComposer,
+    $$SnLocalRealmTableOrderingComposer,
+    $$SnLocalRealmTableAnnotationComposer,
+    $$SnLocalRealmTableCreateCompanionBuilder,
+    $$SnLocalRealmTableUpdateCompanionBuilder,
+    (
+      SnLocalRealmData,
+      BaseReferences<_$AppDatabase, $SnLocalRealmTable, SnLocalRealmData>
+    ),
+    SnLocalRealmData,
+    PrefetchHooks Function()>;
 
 class $AppDatabaseManager {
   final _$AppDatabase _db;
@@ -3908,4 +4447,6 @@ class $AppDatabaseManager {
       $$SnLocalStickerTableTableManager(_db, _db.snLocalSticker);
   $$SnLocalStickerPackTableTableManager get snLocalStickerPack =>
       $$SnLocalStickerPackTableTableManager(_db, _db.snLocalStickerPack);
+  $$SnLocalRealmTableTableManager get snLocalRealm =>
+      $$SnLocalRealmTableTableManager(_db, _db.snLocalRealm);
 }
diff --git a/lib/database/database.steps.dart b/lib/database/database.steps.dart
index eef4f75..ee5ceb6 100644
--- a/lib/database/database.steps.dart
+++ b/lib/database/database.steps.dart
@@ -412,9 +412,214 @@ class Shape8 extends i0.VersionedTable {
       columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
 }
 
+final class Schema4 extends i0.VersionedSchema {
+  Schema4({required super.database}) : super(version: 4);
+  @override
+  late final List<i1.DatabaseSchemaEntity> entities = [
+    snLocalChatChannel,
+    snLocalChatMessage,
+    snLocalChannelMember,
+    snLocalKeyPair,
+    snLocalAccount,
+    snLocalAttachment,
+    snLocalSticker,
+    snLocalStickerPack,
+    snLocalRealm,
+    idxChannelAlias,
+    idxChatChannel,
+    idxAccountName,
+    idxAttachmentRid,
+    idxAttachmentAccount,
+    idxRealmAlias,
+    idxRealmAccount,
+  ];
+  late final Shape0 snLocalChatChannel = Shape0(
+      source: i0.VersionedTable(
+        entityName: 'sn_local_chat_channel',
+        withoutRowId: false,
+        isStrict: false,
+        tableConstraints: [],
+        columns: [
+          _column_0,
+          _column_1,
+          _column_2,
+          _column_3,
+        ],
+        attachedDatabase: database,
+      ),
+      alias: null);
+  late final Shape3 snLocalChatMessage = Shape3(
+      source: i0.VersionedTable(
+        entityName: 'sn_local_chat_message',
+        withoutRowId: false,
+        isStrict: false,
+        tableConstraints: [],
+        columns: [
+          _column_0,
+          _column_4,
+          _column_10,
+          _column_2,
+          _column_3,
+        ],
+        attachedDatabase: database,
+      ),
+      alias: null);
+  late final Shape4 snLocalChannelMember = Shape4(
+      source: i0.VersionedTable(
+        entityName: 'sn_local_channel_member',
+        withoutRowId: false,
+        isStrict: false,
+        tableConstraints: [],
+        columns: [
+          _column_0,
+          _column_4,
+          _column_6,
+          _column_2,
+          _column_3,
+          _column_11,
+        ],
+        attachedDatabase: database,
+      ),
+      alias: null);
+  late final Shape2 snLocalKeyPair = Shape2(
+      source: i0.VersionedTable(
+        entityName: 'sn_local_key_pair',
+        withoutRowId: false,
+        isStrict: false,
+        tableConstraints: [
+          'PRIMARY KEY(id)',
+        ],
+        columns: [
+          _column_5,
+          _column_6,
+          _column_7,
+          _column_8,
+          _column_9,
+        ],
+        attachedDatabase: database,
+      ),
+      alias: null);
+  late final Shape5 snLocalAccount = Shape5(
+      source: i0.VersionedTable(
+        entityName: 'sn_local_account',
+        withoutRowId: false,
+        isStrict: false,
+        tableConstraints: [],
+        columns: [
+          _column_0,
+          _column_12,
+          _column_2,
+          _column_3,
+          _column_11,
+        ],
+        attachedDatabase: database,
+      ),
+      alias: null);
+  late final Shape6 snLocalAttachment = Shape6(
+      source: i0.VersionedTable(
+        entityName: 'sn_local_attachment',
+        withoutRowId: false,
+        isStrict: false,
+        tableConstraints: [],
+        columns: [
+          _column_0,
+          _column_13,
+          _column_14,
+          _column_2,
+          _column_6,
+          _column_3,
+          _column_11,
+        ],
+        attachedDatabase: database,
+      ),
+      alias: null);
+  late final Shape7 snLocalSticker = Shape7(
+      source: i0.VersionedTable(
+        entityName: 'sn_local_sticker',
+        withoutRowId: false,
+        isStrict: false,
+        tableConstraints: [],
+        columns: [
+          _column_0,
+          _column_1,
+          _column_15,
+          _column_2,
+          _column_3,
+        ],
+        attachedDatabase: database,
+      ),
+      alias: null);
+  late final Shape8 snLocalStickerPack = Shape8(
+      source: i0.VersionedTable(
+        entityName: 'sn_local_sticker_pack',
+        withoutRowId: false,
+        isStrict: false,
+        tableConstraints: [],
+        columns: [
+          _column_0,
+          _column_2,
+          _column_3,
+        ],
+        attachedDatabase: database,
+      ),
+      alias: null);
+  late final Shape9 snLocalRealm = Shape9(
+      source: i0.VersionedTable(
+        entityName: 'sn_local_realm',
+        withoutRowId: false,
+        isStrict: false,
+        tableConstraints: [],
+        columns: [
+          _column_0,
+          _column_16,
+          _column_2,
+          _column_6,
+          _column_3,
+          _column_11,
+        ],
+        attachedDatabase: database,
+      ),
+      alias: null);
+  final i1.Index idxChannelAlias = i1.Index('idx_channel_alias',
+      'CREATE INDEX idx_channel_alias ON sn_local_chat_channel (alias)');
+  final i1.Index idxChatChannel = i1.Index('idx_chat_channel',
+      'CREATE INDEX idx_chat_channel ON sn_local_chat_message (channel_id)');
+  final i1.Index idxAccountName = i1.Index('idx_account_name',
+      'CREATE INDEX idx_account_name ON sn_local_account (name)');
+  final i1.Index idxAttachmentRid = i1.Index('idx_attachment_rid',
+      'CREATE INDEX idx_attachment_rid ON sn_local_attachment (rid)');
+  final i1.Index idxAttachmentAccount = i1.Index('idx_attachment_account',
+      'CREATE INDEX idx_attachment_account ON sn_local_attachment (account_id)');
+  final i1.Index idxRealmAlias = i1.Index('idx_realm_alias',
+      'CREATE INDEX idx_realm_alias ON sn_local_realm (alias)');
+  final i1.Index idxRealmAccount = i1.Index('idx_realm_account',
+      'CREATE INDEX idx_realm_account ON sn_local_realm (account_id)');
+}
+
+class Shape9 extends i0.VersionedTable {
+  Shape9({required super.source, required super.alias}) : super.aliased();
+  i1.GeneratedColumn<int> get id =>
+      columnsByName['id']! as i1.GeneratedColumn<int>;
+  i1.GeneratedColumn<String> get alias =>
+      columnsByName['alias']! as i1.GeneratedColumn<String>;
+  i1.GeneratedColumn<String> get content =>
+      columnsByName['content']! as i1.GeneratedColumn<String>;
+  i1.GeneratedColumn<int> get accountId =>
+      columnsByName['account_id']! as i1.GeneratedColumn<int>;
+  i1.GeneratedColumn<DateTime> get createdAt =>
+      columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
+  i1.GeneratedColumn<DateTime> get cacheExpiredAt =>
+      columnsByName['cache_expired_at']! as i1.GeneratedColumn<DateTime>;
+}
+
+i1.GeneratedColumn<String> _column_16(String aliasedName) =>
+    i1.GeneratedColumn<String>('alias', aliasedName, false,
+        type: i1.DriftSqlType.string,
+        defaultConstraints: i1.GeneratedColumn.constraintIsAlways('UNIQUE'));
 i0.MigrationStepWithVersion migrationSteps({
   required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
   required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
+  required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
 }) {
   return (currentVersion, database) async {
     switch (currentVersion) {
@@ -428,6 +633,11 @@ i0.MigrationStepWithVersion migrationSteps({
         final migrator = i1.Migrator(database, schema);
         await from2To3(migrator, schema);
         return 3;
+      case 3:
+        final schema = Schema4(database: database);
+        final migrator = i1.Migrator(database, schema);
+        await from3To4(migrator, schema);
+        return 4;
       default:
         throw ArgumentError.value('Unknown migration from $currentVersion');
     }
@@ -437,9 +647,11 @@ i0.MigrationStepWithVersion migrationSteps({
 i1.OnUpgrade stepByStep({
   required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
   required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
+  required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
 }) =>
     i0.VersionedSchema.stepByStepHelper(
         step: migrationSteps(
       from1To2: from1To2,
       from2To3: from2To3,
+      from3To4: from3To4,
     ));
diff --git a/lib/database/realm.dart b/lib/database/realm.dart
new file mode 100644
index 0000000..f5ee2e8
--- /dev/null
+++ b/lib/database/realm.dart
@@ -0,0 +1,45 @@
+import 'dart:convert';
+
+import 'package:drift/drift.dart';
+import 'package:surface/types/realm.dart';
+
+class SnRealmConverter extends TypeConverter<SnRealm, String>
+    with JsonTypeConverter2<SnRealm, String, Map<String, Object?>> {
+  const SnRealmConverter();
+
+  @override
+  SnRealm fromSql(String fromDb) {
+    return fromJson(jsonDecode(fromDb) as Map<String, dynamic>);
+  }
+
+  @override
+  String toSql(SnRealm value) {
+    return jsonEncode(toJson(value));
+  }
+
+  @override
+  SnRealm fromJson(Map<String, Object?> json) {
+    return SnRealm.fromJson(json);
+  }
+
+  @override
+  Map<String, Object?> toJson(SnRealm value) {
+    return value.toJson();
+  }
+}
+
+@TableIndex(name: 'idx_realm_alias', columns: {#alias})
+@TableIndex(name: 'idx_realm_account', columns: {#accountId})
+class SnLocalRealm extends Table {
+  IntColumn get id => integer().autoIncrement()();
+
+  TextColumn get alias => text().unique()();
+
+  TextColumn get content => text().map(const SnRealmConverter())();
+
+  IntColumn get accountId => integer()();
+
+  DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
+
+  DateTimeColumn get cacheExpiredAt => dateTime()();
+}
diff --git a/lib/providers/sn_attachment.dart b/lib/providers/sn_attachment.dart
index 9e1515b..bccb7bd 100644
--- a/lib/providers/sn_attachment.dart
+++ b/lib/providers/sn_attachment.dart
@@ -321,13 +321,13 @@ class SnAttachmentProvider {
           uuid: ele.uuid,
           content: ele,
           accountId: ele.accountId,
-          cacheExpiredAt: DateTime.now().add(const Duration(days: 7)),
+          cacheExpiredAt: DateTime.now().add(const Duration(hours: 1)),
         ),
         onConflict: DoUpdate(
           (_) => SnLocalAttachmentCompanion.custom(
             content: Constant(jsonEncode(ele.toJson())),
             cacheExpiredAt:
-                Constant(DateTime.now().add(const Duration(days: 7))),
+                Constant(DateTime.now().add(const Duration(hours: 1))),
           ),
         ),
       );
diff --git a/lib/providers/sn_realm.dart b/lib/providers/sn_realm.dart
index 36afda4..efb3dde 100644
--- a/lib/providers/sn_realm.dart
+++ b/lib/providers/sn_realm.dart
@@ -1,13 +1,20 @@
+import 'dart:convert';
+
+import 'package:drift/drift.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
+import 'package:surface/database/database.dart';
+import 'package:surface/providers/database.dart';
 import 'package:surface/providers/sn_network.dart';
 import 'package:surface/types/realm.dart';
 
 class SnRealmProvider {
   late final SnNetworkProvider _sn;
+  late final DatabaseProvider _dt;
 
   SnRealmProvider(BuildContext context) {
     _sn = context.read<SnNetworkProvider>();
+    _dt = context.read<DatabaseProvider>();
   }
 
   final Map<String, SnRealm> _cache = {};
@@ -28,6 +35,7 @@ class SnRealmProvider {
       _cache[realm.alias] = realm;
       _cache[realm.id.toString()] = realm;
     }
+    _saveToLocal(out);
     return out;
   }
 
@@ -35,10 +43,43 @@ class SnRealmProvider {
     if (_cache.containsKey(aliasOrId.toString())) {
       return _cache[aliasOrId.toString()]!;
     }
+    final localResp = await (_dt.db.snLocalRealm.select()
+          ..where((e) =>
+              e.id.equals(aliasOrId is int ? aliasOrId : 0) |
+              e.alias.equals(aliasOrId.toString()))
+          ..where((e) => e.cacheExpiredAt.isBiggerThanValue(DateTime.now())))
+        .getSingleOrNull();
+    if (localResp != null) {
+      _cache[localResp.content.id.toString()] = localResp.content;
+      _cache[localResp.content.alias] = localResp.content;
+      return localResp.content;
+    }
     final resp = await _sn.client.get('/cgi/id/realms/$aliasOrId');
     final out = SnRealm.fromJson(resp.data);
     _cache[out.alias] = out;
     _cache[out.id.toString()] = out;
+    _saveToLocal([out]);
     return out;
   }
+
+  Future<void> _saveToLocal(Iterable<SnRealm> out) async {
+    for (final ele in out) {
+      await _dt.db.snLocalRealm.insertOne(
+        SnLocalRealmCompanion.insert(
+          id: Value(ele.id),
+          alias: ele.alias,
+          content: ele,
+          accountId: ele.accountId,
+          cacheExpiredAt: DateTime.now().add(const Duration(hours: 1)),
+        ),
+        onConflict: DoUpdate(
+          (_) => SnLocalRealmCompanion.custom(
+            content: Constant(jsonEncode(ele.toJson())),
+            cacheExpiredAt:
+                Constant(DateTime.now().add(const Duration(hours: 1))),
+          ),
+        ),
+      );
+    }
+  }
 }
diff --git a/lib/screens/chat/room.dart b/lib/screens/chat/room.dart
index d9295e1..22950cb 100644
--- a/lib/screens/chat/room.dart
+++ b/lib/screens/chat/room.dart
@@ -52,8 +52,10 @@ class ChatRoomScreen extends StatefulWidget {
 class _ChatRoomScreenState extends State<ChatRoomScreen> {
   bool _isBusy = false;
   bool _isCalling = false;
+  bool _isJoining = false;
 
   SnChannel? _channel;
+  SnChannelMember? _currentMember;
   SnChannelMember? _otherMember;
   SnChatCall? _ongoingCall;
 
@@ -67,7 +69,24 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
 
   StreamSubscription? _wsSubscription;
 
-  // TODO fetch user identity and ask them to join the channel or not
+  Future<void> _joinChannel() async {
+    try {
+      setState(() => _isJoining = true);
+      final sn = context.read<SnNetworkProvider>();
+      final ua = context.read<UserProvider>();
+      await sn.client
+          .post('/cgi/im/channels/${_channel!.keyPath}/members', data: {
+        'related': ua.user?.name,
+      });
+      _initializeChat();
+    } catch (err) {
+      if (!mounted) return;
+      context.showErrorDialog(err);
+    } finally {
+      setState(() => _isJoining = true);
+    }
+  }
+
   Future<void> _fetchChannel() async {
     setState(() => _isBusy = true);
 
@@ -76,6 +95,12 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
       _channel = await chan.getChannel('${widget.scope}:${widget.alias}');
 
       if (!mounted || _channel == null) return;
+      final ct = context.read<ChatChannelProvider>();
+      try {
+        _currentMember = await ct.getChannelProfile(_channel!);
+      } catch (_) {}
+
+      if (!mounted || _currentMember == null) return;
       final ud = context.read<UserDirectoryProvider>();
       final ua = context.read<UserProvider>();
       if (_channel!.type == 1) {
@@ -204,11 +229,9 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
     return a.createdAt.difference(b.createdAt).inMinutes <= 3;
   }
 
-  @override
-  void initState() {
-    super.initState();
-    _messageController = ChatMessageController(context);
+  Future<void> _initializeChat() async {
     _fetchChannel().then((_) async {
+      if (_currentMember == null) return;
       await _messageController.initialize(_channel!);
 
       if (widget.extra != null) {
@@ -230,6 +253,13 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
         _fetchOngoingCall(),
       ]);
     });
+  }
+
+  @override
+  void initState() {
+    super.initState();
+    _messageController = ChatMessageController(context);
+    _initializeChat();
 
     _wsSubscription = _ws.pk.stream.listen((event) {
       switch (event.method) {
@@ -281,25 +311,27 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
               : _channel?.name ?? 'loading'.tr(),
         ),
         actions: [
-          IconButton(
-            onPressed: () {
-              setState(() => _isEncrypted = !_isEncrypted);
-              _inputGlobalKey.currentState?.setEncrypted(_isEncrypted);
-            },
-            icon: _isEncrypted
-                ? const Icon(Symbols.lock)
-                : const Icon(Symbols.no_encryption),
-          ),
-          IconButton(
-            icon: _ongoingCall == null
-                ? const Icon(Symbols.call)
-                : const Icon(Symbols.call_end),
-            onPressed: _isCalling
-                ? null
-                : _ongoingCall == null
-                    ? _makeCall
-                    : _endCall,
-          ),
+          if (_currentMember != null)
+            IconButton(
+              onPressed: () {
+                setState(() => _isEncrypted = !_isEncrypted);
+                _inputGlobalKey.currentState?.setEncrypted(_isEncrypted);
+              },
+              icon: _isEncrypted
+                  ? const Icon(Symbols.lock)
+                  : const Icon(Symbols.no_encryption),
+            ),
+          if (_currentMember != null)
+            IconButton(
+              icon: _ongoingCall == null
+                  ? const Icon(Symbols.call)
+                  : const Icon(Symbols.call_end),
+              onPressed: _isCalling
+                  ? null
+                  : _ongoingCall == null
+                      ? _makeCall
+                      : _endCall,
+            ),
           IconButton(
             icon: const Icon(Symbols.more_vert),
             onPressed: () {
@@ -348,7 +380,41 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
               ).height(_ongoingCall != null ? 54 : 0, animate: true).animate(
                   const Duration(milliseconds: 300),
                   Curves.fastLinearToSlowEaseIn),
-              if (_messageController.isPending)
+              if (_currentMember == null && !_isBusy)
+                Expanded(
+                  child: Center(
+                    child: Container(
+                      constraints: const BoxConstraints(maxWidth: 280),
+                      child: Column(
+                        mainAxisSize: MainAxisSize.min,
+                        children: [
+                          const Icon(Symbols.person_remove, size: 40, fill: 1),
+                          const Gap(8),
+                          Text('chatUnjoined'.tr(), textAlign: TextAlign.center)
+                              .fontSize(16)
+                              .bold(),
+                          Text('chatUnjoinedDescription'.tr(),
+                                  textAlign: TextAlign.center)
+                              .fontSize(13),
+                          if (_channel!.isPublic)
+                            Text('chatUnjoinedPublicDescription'.tr(),
+                                    textAlign: TextAlign.center)
+                                .fontSize(13)
+                                .padding(top: 8),
+                          if (_channel!.isPublic)
+                            TextButton(
+                              style: ButtonStyle(
+                                visualDensity: VisualDensity.compact,
+                              ),
+                              onPressed: _isJoining ? null : _joinChannel,
+                              child: Text('chatJoin').tr(),
+                            ),
+                        ],
+                      ),
+                    ),
+                  ),
+                )
+              else if (_messageController.isPending)
                 Expanded(
                   child: const CircularProgressIndicator().center(),
                 )
@@ -403,7 +469,7 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
                     },
                   ),
                 ),
-              if (!_messageController.isPending)
+              if (!_messageController.isPending && _currentMember != null)
                 Material(
                   elevation: 2,
                   child: Column(
diff --git a/lib/widgets/navigation/app_drawer_navigation.dart b/lib/widgets/navigation/app_drawer_navigation.dart
index 015776e..30327c9 100644
--- a/lib/widgets/navigation/app_drawer_navigation.dart
+++ b/lib/widgets/navigation/app_drawer_navigation.dart
@@ -11,6 +11,7 @@ import 'package:provider/provider.dart';
 import 'package:styled_widget/styled_widget.dart';
 import 'package:surface/providers/config.dart';
 import 'package:surface/providers/navigation.dart';
+import 'package:surface/providers/sn_realm.dart';
 import 'package:surface/providers/userinfo.dart';
 import 'package:surface/widgets/account/account_image.dart';
 import 'package:surface/widgets/version_label.dart';
@@ -40,6 +41,7 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
     final ua = context.read<UserProvider>();
     final nav = context.watch<NavigationProvider>();
     final cfg = context.watch<ConfigProvider>();
+    final rel = context.read<SnRealmProvider>();
 
     final backgroundColor = cfg.drawerIsExpanded ? Colors.transparent : null;
 
@@ -82,7 +84,31 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
                 vertical: 12,
               ),
               Expanded(
-                child: ListView(),
+                child: ListView(
+                  padding: EdgeInsets.zero,
+                  children: [
+                    ...rel.availableRealms.map((ele) {
+                      return ListTile(
+                        minTileHeight: 48,
+                        contentPadding: EdgeInsets.symmetric(horizontal: 24),
+                        leading: AccountImage(
+                          content: ele.avatar,
+                          radius: 16,
+                        ),
+                        title: Text(ele.name),
+                        onTap: () {
+                          GoRouter.of(context).goNamed(
+                            'realmDetail',
+                            pathParameters: {
+                              'alias': ele.alias,
+                            },
+                          );
+                          Scaffold.of(context).closeDrawer();
+                        },
+                      );
+                    }),
+                  ],
+                ),
               ),
               Row(
                 spacing: 8,
@@ -106,7 +132,7 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
                 child: ListTile(
                   contentPadding: EdgeInsets.symmetric(horizontal: 24),
                   leading: AccountImage(content: ua.user?.avatar),
-                  title: Text(ua.user?.nick ?? 'unknown').tr().fontSize(15),
+                  title: Text(ua.user?.nick ?? 'unknown'.tr()).fontSize(15),
                   subtitle:
                       Text('@${ua.user?.name ?? 'unknown'.tr()}').fontSize(13),
                   trailing: Row(
diff --git a/test/drift/my_database/generated/schema.dart b/test/drift/my_database/generated/schema.dart
index 209e70d..22131b1 100644
--- a/test/drift/my_database/generated/schema.dart
+++ b/test/drift/my_database/generated/schema.dart
@@ -6,6 +6,7 @@ import 'package:drift/internal/migrations.dart';
 import 'schema_v1.dart' as v1;
 import 'schema_v2.dart' as v2;
 import 'schema_v3.dart' as v3;
+import 'schema_v4.dart' as v4;
 
 class GeneratedHelper implements SchemaInstantiationHelper {
   @override
@@ -17,10 +18,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
         return v2.DatabaseAtV2(db);
       case 3:
         return v3.DatabaseAtV3(db);
+      case 4:
+        return v4.DatabaseAtV4(db);
       default:
         throw MissingSchemaException(version, versions);
     }
   }
 
-  static const versions = const [1, 2, 3];
+  static const versions = const [1, 2, 3, 4];
 }
diff --git a/test/drift/my_database/generated/schema_v4.dart b/test/drift/my_database/generated/schema_v4.dart
new file mode 100644
index 0000000..5dbfd0b
--- /dev/null
+++ b/test/drift/my_database/generated/schema_v4.dart
@@ -0,0 +1,2391 @@
+// dart format width=80
+// GENERATED CODE, DO NOT EDIT BY HAND.
+// ignore_for_file: type=lint
+import 'package:drift/drift.dart';
+
+class SnLocalChatChannel extends Table
+    with TableInfo<SnLocalChatChannel, SnLocalChatChannelData> {
+  @override
+  final GeneratedDatabase attachedDatabase;
+  final String? _alias;
+  SnLocalChatChannel(this.attachedDatabase, [this._alias]);
+  late final GeneratedColumn<int> id = GeneratedColumn<int>(
+      'id', aliasedName, false,
+      hasAutoIncrement: true,
+      type: DriftSqlType.int,
+      requiredDuringInsert: false,
+      defaultConstraints:
+          GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
+  late final GeneratedColumn<String> alias = GeneratedColumn<String>(
+      'alias', aliasedName, false,
+      type: DriftSqlType.string, requiredDuringInsert: true);
+  late final GeneratedColumn<String> content = GeneratedColumn<String>(
+      'content', aliasedName, false,
+      type: DriftSqlType.string, requiredDuringInsert: true);
+  late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
+      'created_at', aliasedName, false,
+      type: DriftSqlType.dateTime,
+      requiredDuringInsert: false,
+      defaultValue: const CustomExpression(
+          'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)'));
+  @override
+  List<GeneratedColumn> get $columns => [id, alias, content, createdAt];
+  @override
+  String get aliasedName => _alias ?? actualTableName;
+  @override
+  String get actualTableName => $name;
+  static const String $name = 'sn_local_chat_channel';
+  @override
+  Set<GeneratedColumn> get $primaryKey => {id};
+  @override
+  SnLocalChatChannelData map(Map<String, dynamic> data, {String? tablePrefix}) {
+    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+    return SnLocalChatChannelData(
+      id: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}id'])!,
+      alias: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}alias'])!,
+      content: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}content'])!,
+      createdAt: attachedDatabase.typeMapping
+          .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
+    );
+  }
+
+  @override
+  SnLocalChatChannel createAlias(String alias) {
+    return SnLocalChatChannel(attachedDatabase, alias);
+  }
+}
+
+class SnLocalChatChannelData extends DataClass
+    implements Insertable<SnLocalChatChannelData> {
+  final int id;
+  final String alias;
+  final String content;
+  final DateTime createdAt;
+  const SnLocalChatChannelData(
+      {required this.id,
+      required this.alias,
+      required this.content,
+      required this.createdAt});
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    map['id'] = Variable<int>(id);
+    map['alias'] = Variable<String>(alias);
+    map['content'] = Variable<String>(content);
+    map['created_at'] = Variable<DateTime>(createdAt);
+    return map;
+  }
+
+  SnLocalChatChannelCompanion toCompanion(bool nullToAbsent) {
+    return SnLocalChatChannelCompanion(
+      id: Value(id),
+      alias: Value(alias),
+      content: Value(content),
+      createdAt: Value(createdAt),
+    );
+  }
+
+  factory SnLocalChatChannelData.fromJson(Map<String, dynamic> json,
+      {ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return SnLocalChatChannelData(
+      id: serializer.fromJson<int>(json['id']),
+      alias: serializer.fromJson<String>(json['alias']),
+      content: serializer.fromJson<String>(json['content']),
+      createdAt: serializer.fromJson<DateTime>(json['createdAt']),
+    );
+  }
+  @override
+  Map<String, dynamic> toJson({ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return <String, dynamic>{
+      'id': serializer.toJson<int>(id),
+      'alias': serializer.toJson<String>(alias),
+      'content': serializer.toJson<String>(content),
+      'createdAt': serializer.toJson<DateTime>(createdAt),
+    };
+  }
+
+  SnLocalChatChannelData copyWith(
+          {int? id, String? alias, String? content, DateTime? createdAt}) =>
+      SnLocalChatChannelData(
+        id: id ?? this.id,
+        alias: alias ?? this.alias,
+        content: content ?? this.content,
+        createdAt: createdAt ?? this.createdAt,
+      );
+  SnLocalChatChannelData copyWithCompanion(SnLocalChatChannelCompanion data) {
+    return SnLocalChatChannelData(
+      id: data.id.present ? data.id.value : this.id,
+      alias: data.alias.present ? data.alias.value : this.alias,
+      content: data.content.present ? data.content.value : this.content,
+      createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
+    );
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalChatChannelData(')
+          ..write('id: $id, ')
+          ..write('alias: $alias, ')
+          ..write('content: $content, ')
+          ..write('createdAt: $createdAt')
+          ..write(')'))
+        .toString();
+  }
+
+  @override
+  int get hashCode => Object.hash(id, alias, content, createdAt);
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      (other is SnLocalChatChannelData &&
+          other.id == this.id &&
+          other.alias == this.alias &&
+          other.content == this.content &&
+          other.createdAt == this.createdAt);
+}
+
+class SnLocalChatChannelCompanion
+    extends UpdateCompanion<SnLocalChatChannelData> {
+  final Value<int> id;
+  final Value<String> alias;
+  final Value<String> content;
+  final Value<DateTime> createdAt;
+  const SnLocalChatChannelCompanion({
+    this.id = const Value.absent(),
+    this.alias = const Value.absent(),
+    this.content = const Value.absent(),
+    this.createdAt = const Value.absent(),
+  });
+  SnLocalChatChannelCompanion.insert({
+    this.id = const Value.absent(),
+    required String alias,
+    required String content,
+    this.createdAt = const Value.absent(),
+  })  : alias = Value(alias),
+        content = Value(content);
+  static Insertable<SnLocalChatChannelData> custom({
+    Expression<int>? id,
+    Expression<String>? alias,
+    Expression<String>? content,
+    Expression<DateTime>? createdAt,
+  }) {
+    return RawValuesInsertable({
+      if (id != null) 'id': id,
+      if (alias != null) 'alias': alias,
+      if (content != null) 'content': content,
+      if (createdAt != null) 'created_at': createdAt,
+    });
+  }
+
+  SnLocalChatChannelCompanion copyWith(
+      {Value<int>? id,
+      Value<String>? alias,
+      Value<String>? content,
+      Value<DateTime>? createdAt}) {
+    return SnLocalChatChannelCompanion(
+      id: id ?? this.id,
+      alias: alias ?? this.alias,
+      content: content ?? this.content,
+      createdAt: createdAt ?? this.createdAt,
+    );
+  }
+
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    if (id.present) {
+      map['id'] = Variable<int>(id.value);
+    }
+    if (alias.present) {
+      map['alias'] = Variable<String>(alias.value);
+    }
+    if (content.present) {
+      map['content'] = Variable<String>(content.value);
+    }
+    if (createdAt.present) {
+      map['created_at'] = Variable<DateTime>(createdAt.value);
+    }
+    return map;
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalChatChannelCompanion(')
+          ..write('id: $id, ')
+          ..write('alias: $alias, ')
+          ..write('content: $content, ')
+          ..write('createdAt: $createdAt')
+          ..write(')'))
+        .toString();
+  }
+}
+
+class SnLocalChatMessage extends Table
+    with TableInfo<SnLocalChatMessage, SnLocalChatMessageData> {
+  @override
+  final GeneratedDatabase attachedDatabase;
+  final String? _alias;
+  SnLocalChatMessage(this.attachedDatabase, [this._alias]);
+  late final GeneratedColumn<int> id = GeneratedColumn<int>(
+      'id', aliasedName, false,
+      hasAutoIncrement: true,
+      type: DriftSqlType.int,
+      requiredDuringInsert: false,
+      defaultConstraints:
+          GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
+  late final GeneratedColumn<int> channelId = GeneratedColumn<int>(
+      'channel_id', aliasedName, false,
+      type: DriftSqlType.int, requiredDuringInsert: true);
+  late final GeneratedColumn<int> senderId = GeneratedColumn<int>(
+      'sender_id', aliasedName, true,
+      type: DriftSqlType.int, requiredDuringInsert: false);
+  late final GeneratedColumn<String> content = GeneratedColumn<String>(
+      'content', aliasedName, false,
+      type: DriftSqlType.string, requiredDuringInsert: true);
+  late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
+      'created_at', aliasedName, false,
+      type: DriftSqlType.dateTime,
+      requiredDuringInsert: false,
+      defaultValue: const CustomExpression(
+          'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)'));
+  @override
+  List<GeneratedColumn> get $columns =>
+      [id, channelId, senderId, content, createdAt];
+  @override
+  String get aliasedName => _alias ?? actualTableName;
+  @override
+  String get actualTableName => $name;
+  static const String $name = 'sn_local_chat_message';
+  @override
+  Set<GeneratedColumn> get $primaryKey => {id};
+  @override
+  SnLocalChatMessageData map(Map<String, dynamic> data, {String? tablePrefix}) {
+    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+    return SnLocalChatMessageData(
+      id: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}id'])!,
+      channelId: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}channel_id'])!,
+      senderId: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}sender_id']),
+      content: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}content'])!,
+      createdAt: attachedDatabase.typeMapping
+          .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
+    );
+  }
+
+  @override
+  SnLocalChatMessage createAlias(String alias) {
+    return SnLocalChatMessage(attachedDatabase, alias);
+  }
+}
+
+class SnLocalChatMessageData extends DataClass
+    implements Insertable<SnLocalChatMessageData> {
+  final int id;
+  final int channelId;
+  final int? senderId;
+  final String content;
+  final DateTime createdAt;
+  const SnLocalChatMessageData(
+      {required this.id,
+      required this.channelId,
+      this.senderId,
+      required this.content,
+      required this.createdAt});
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    map['id'] = Variable<int>(id);
+    map['channel_id'] = Variable<int>(channelId);
+    if (!nullToAbsent || senderId != null) {
+      map['sender_id'] = Variable<int>(senderId);
+    }
+    map['content'] = Variable<String>(content);
+    map['created_at'] = Variable<DateTime>(createdAt);
+    return map;
+  }
+
+  SnLocalChatMessageCompanion toCompanion(bool nullToAbsent) {
+    return SnLocalChatMessageCompanion(
+      id: Value(id),
+      channelId: Value(channelId),
+      senderId: senderId == null && nullToAbsent
+          ? const Value.absent()
+          : Value(senderId),
+      content: Value(content),
+      createdAt: Value(createdAt),
+    );
+  }
+
+  factory SnLocalChatMessageData.fromJson(Map<String, dynamic> json,
+      {ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return SnLocalChatMessageData(
+      id: serializer.fromJson<int>(json['id']),
+      channelId: serializer.fromJson<int>(json['channelId']),
+      senderId: serializer.fromJson<int?>(json['senderId']),
+      content: serializer.fromJson<String>(json['content']),
+      createdAt: serializer.fromJson<DateTime>(json['createdAt']),
+    );
+  }
+  @override
+  Map<String, dynamic> toJson({ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return <String, dynamic>{
+      'id': serializer.toJson<int>(id),
+      'channelId': serializer.toJson<int>(channelId),
+      'senderId': serializer.toJson<int?>(senderId),
+      'content': serializer.toJson<String>(content),
+      'createdAt': serializer.toJson<DateTime>(createdAt),
+    };
+  }
+
+  SnLocalChatMessageData copyWith(
+          {int? id,
+          int? channelId,
+          Value<int?> senderId = const Value.absent(),
+          String? content,
+          DateTime? createdAt}) =>
+      SnLocalChatMessageData(
+        id: id ?? this.id,
+        channelId: channelId ?? this.channelId,
+        senderId: senderId.present ? senderId.value : this.senderId,
+        content: content ?? this.content,
+        createdAt: createdAt ?? this.createdAt,
+      );
+  SnLocalChatMessageData copyWithCompanion(SnLocalChatMessageCompanion data) {
+    return SnLocalChatMessageData(
+      id: data.id.present ? data.id.value : this.id,
+      channelId: data.channelId.present ? data.channelId.value : this.channelId,
+      senderId: data.senderId.present ? data.senderId.value : this.senderId,
+      content: data.content.present ? data.content.value : this.content,
+      createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
+    );
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalChatMessageData(')
+          ..write('id: $id, ')
+          ..write('channelId: $channelId, ')
+          ..write('senderId: $senderId, ')
+          ..write('content: $content, ')
+          ..write('createdAt: $createdAt')
+          ..write(')'))
+        .toString();
+  }
+
+  @override
+  int get hashCode => Object.hash(id, channelId, senderId, content, createdAt);
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      (other is SnLocalChatMessageData &&
+          other.id == this.id &&
+          other.channelId == this.channelId &&
+          other.senderId == this.senderId &&
+          other.content == this.content &&
+          other.createdAt == this.createdAt);
+}
+
+class SnLocalChatMessageCompanion
+    extends UpdateCompanion<SnLocalChatMessageData> {
+  final Value<int> id;
+  final Value<int> channelId;
+  final Value<int?> senderId;
+  final Value<String> content;
+  final Value<DateTime> createdAt;
+  const SnLocalChatMessageCompanion({
+    this.id = const Value.absent(),
+    this.channelId = const Value.absent(),
+    this.senderId = const Value.absent(),
+    this.content = const Value.absent(),
+    this.createdAt = const Value.absent(),
+  });
+  SnLocalChatMessageCompanion.insert({
+    this.id = const Value.absent(),
+    required int channelId,
+    this.senderId = const Value.absent(),
+    required String content,
+    this.createdAt = const Value.absent(),
+  })  : channelId = Value(channelId),
+        content = Value(content);
+  static Insertable<SnLocalChatMessageData> custom({
+    Expression<int>? id,
+    Expression<int>? channelId,
+    Expression<int>? senderId,
+    Expression<String>? content,
+    Expression<DateTime>? createdAt,
+  }) {
+    return RawValuesInsertable({
+      if (id != null) 'id': id,
+      if (channelId != null) 'channel_id': channelId,
+      if (senderId != null) 'sender_id': senderId,
+      if (content != null) 'content': content,
+      if (createdAt != null) 'created_at': createdAt,
+    });
+  }
+
+  SnLocalChatMessageCompanion copyWith(
+      {Value<int>? id,
+      Value<int>? channelId,
+      Value<int?>? senderId,
+      Value<String>? content,
+      Value<DateTime>? createdAt}) {
+    return SnLocalChatMessageCompanion(
+      id: id ?? this.id,
+      channelId: channelId ?? this.channelId,
+      senderId: senderId ?? this.senderId,
+      content: content ?? this.content,
+      createdAt: createdAt ?? this.createdAt,
+    );
+  }
+
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    if (id.present) {
+      map['id'] = Variable<int>(id.value);
+    }
+    if (channelId.present) {
+      map['channel_id'] = Variable<int>(channelId.value);
+    }
+    if (senderId.present) {
+      map['sender_id'] = Variable<int>(senderId.value);
+    }
+    if (content.present) {
+      map['content'] = Variable<String>(content.value);
+    }
+    if (createdAt.present) {
+      map['created_at'] = Variable<DateTime>(createdAt.value);
+    }
+    return map;
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalChatMessageCompanion(')
+          ..write('id: $id, ')
+          ..write('channelId: $channelId, ')
+          ..write('senderId: $senderId, ')
+          ..write('content: $content, ')
+          ..write('createdAt: $createdAt')
+          ..write(')'))
+        .toString();
+  }
+}
+
+class SnLocalChannelMember extends Table
+    with TableInfo<SnLocalChannelMember, SnLocalChannelMemberData> {
+  @override
+  final GeneratedDatabase attachedDatabase;
+  final String? _alias;
+  SnLocalChannelMember(this.attachedDatabase, [this._alias]);
+  late final GeneratedColumn<int> id = GeneratedColumn<int>(
+      'id', aliasedName, false,
+      hasAutoIncrement: true,
+      type: DriftSqlType.int,
+      requiredDuringInsert: false,
+      defaultConstraints:
+          GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
+  late final GeneratedColumn<int> channelId = GeneratedColumn<int>(
+      'channel_id', aliasedName, false,
+      type: DriftSqlType.int, requiredDuringInsert: true);
+  late final GeneratedColumn<int> accountId = GeneratedColumn<int>(
+      'account_id', aliasedName, false,
+      type: DriftSqlType.int, requiredDuringInsert: true);
+  late final GeneratedColumn<String> content = GeneratedColumn<String>(
+      'content', aliasedName, false,
+      type: DriftSqlType.string, requiredDuringInsert: true);
+  late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
+      'created_at', aliasedName, false,
+      type: DriftSqlType.dateTime,
+      requiredDuringInsert: false,
+      defaultValue: const CustomExpression(
+          'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)'));
+  late final GeneratedColumn<DateTime> cacheExpiredAt =
+      GeneratedColumn<DateTime>('cache_expired_at', aliasedName, false,
+          type: DriftSqlType.dateTime, requiredDuringInsert: true);
+  @override
+  List<GeneratedColumn> get $columns =>
+      [id, channelId, accountId, content, createdAt, cacheExpiredAt];
+  @override
+  String get aliasedName => _alias ?? actualTableName;
+  @override
+  String get actualTableName => $name;
+  static const String $name = 'sn_local_channel_member';
+  @override
+  Set<GeneratedColumn> get $primaryKey => {id};
+  @override
+  SnLocalChannelMemberData map(Map<String, dynamic> data,
+      {String? tablePrefix}) {
+    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+    return SnLocalChannelMemberData(
+      id: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}id'])!,
+      channelId: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}channel_id'])!,
+      accountId: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}account_id'])!,
+      content: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}content'])!,
+      createdAt: attachedDatabase.typeMapping
+          .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
+      cacheExpiredAt: attachedDatabase.typeMapping.read(
+          DriftSqlType.dateTime, data['${effectivePrefix}cache_expired_at'])!,
+    );
+  }
+
+  @override
+  SnLocalChannelMember createAlias(String alias) {
+    return SnLocalChannelMember(attachedDatabase, alias);
+  }
+}
+
+class SnLocalChannelMemberData extends DataClass
+    implements Insertable<SnLocalChannelMemberData> {
+  final int id;
+  final int channelId;
+  final int accountId;
+  final String content;
+  final DateTime createdAt;
+  final DateTime cacheExpiredAt;
+  const SnLocalChannelMemberData(
+      {required this.id,
+      required this.channelId,
+      required this.accountId,
+      required this.content,
+      required this.createdAt,
+      required this.cacheExpiredAt});
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    map['id'] = Variable<int>(id);
+    map['channel_id'] = Variable<int>(channelId);
+    map['account_id'] = Variable<int>(accountId);
+    map['content'] = Variable<String>(content);
+    map['created_at'] = Variable<DateTime>(createdAt);
+    map['cache_expired_at'] = Variable<DateTime>(cacheExpiredAt);
+    return map;
+  }
+
+  SnLocalChannelMemberCompanion toCompanion(bool nullToAbsent) {
+    return SnLocalChannelMemberCompanion(
+      id: Value(id),
+      channelId: Value(channelId),
+      accountId: Value(accountId),
+      content: Value(content),
+      createdAt: Value(createdAt),
+      cacheExpiredAt: Value(cacheExpiredAt),
+    );
+  }
+
+  factory SnLocalChannelMemberData.fromJson(Map<String, dynamic> json,
+      {ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return SnLocalChannelMemberData(
+      id: serializer.fromJson<int>(json['id']),
+      channelId: serializer.fromJson<int>(json['channelId']),
+      accountId: serializer.fromJson<int>(json['accountId']),
+      content: serializer.fromJson<String>(json['content']),
+      createdAt: serializer.fromJson<DateTime>(json['createdAt']),
+      cacheExpiredAt: serializer.fromJson<DateTime>(json['cacheExpiredAt']),
+    );
+  }
+  @override
+  Map<String, dynamic> toJson({ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return <String, dynamic>{
+      'id': serializer.toJson<int>(id),
+      'channelId': serializer.toJson<int>(channelId),
+      'accountId': serializer.toJson<int>(accountId),
+      'content': serializer.toJson<String>(content),
+      'createdAt': serializer.toJson<DateTime>(createdAt),
+      'cacheExpiredAt': serializer.toJson<DateTime>(cacheExpiredAt),
+    };
+  }
+
+  SnLocalChannelMemberData copyWith(
+          {int? id,
+          int? channelId,
+          int? accountId,
+          String? content,
+          DateTime? createdAt,
+          DateTime? cacheExpiredAt}) =>
+      SnLocalChannelMemberData(
+        id: id ?? this.id,
+        channelId: channelId ?? this.channelId,
+        accountId: accountId ?? this.accountId,
+        content: content ?? this.content,
+        createdAt: createdAt ?? this.createdAt,
+        cacheExpiredAt: cacheExpiredAt ?? this.cacheExpiredAt,
+      );
+  SnLocalChannelMemberData copyWithCompanion(
+      SnLocalChannelMemberCompanion data) {
+    return SnLocalChannelMemberData(
+      id: data.id.present ? data.id.value : this.id,
+      channelId: data.channelId.present ? data.channelId.value : this.channelId,
+      accountId: data.accountId.present ? data.accountId.value : this.accountId,
+      content: data.content.present ? data.content.value : this.content,
+      createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
+      cacheExpiredAt: data.cacheExpiredAt.present
+          ? data.cacheExpiredAt.value
+          : this.cacheExpiredAt,
+    );
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalChannelMemberData(')
+          ..write('id: $id, ')
+          ..write('channelId: $channelId, ')
+          ..write('accountId: $accountId, ')
+          ..write('content: $content, ')
+          ..write('createdAt: $createdAt, ')
+          ..write('cacheExpiredAt: $cacheExpiredAt')
+          ..write(')'))
+        .toString();
+  }
+
+  @override
+  int get hashCode =>
+      Object.hash(id, channelId, accountId, content, createdAt, cacheExpiredAt);
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      (other is SnLocalChannelMemberData &&
+          other.id == this.id &&
+          other.channelId == this.channelId &&
+          other.accountId == this.accountId &&
+          other.content == this.content &&
+          other.createdAt == this.createdAt &&
+          other.cacheExpiredAt == this.cacheExpiredAt);
+}
+
+class SnLocalChannelMemberCompanion
+    extends UpdateCompanion<SnLocalChannelMemberData> {
+  final Value<int> id;
+  final Value<int> channelId;
+  final Value<int> accountId;
+  final Value<String> content;
+  final Value<DateTime> createdAt;
+  final Value<DateTime> cacheExpiredAt;
+  const SnLocalChannelMemberCompanion({
+    this.id = const Value.absent(),
+    this.channelId = const Value.absent(),
+    this.accountId = const Value.absent(),
+    this.content = const Value.absent(),
+    this.createdAt = const Value.absent(),
+    this.cacheExpiredAt = const Value.absent(),
+  });
+  SnLocalChannelMemberCompanion.insert({
+    this.id = const Value.absent(),
+    required int channelId,
+    required int accountId,
+    required String content,
+    this.createdAt = const Value.absent(),
+    required DateTime cacheExpiredAt,
+  })  : channelId = Value(channelId),
+        accountId = Value(accountId),
+        content = Value(content),
+        cacheExpiredAt = Value(cacheExpiredAt);
+  static Insertable<SnLocalChannelMemberData> custom({
+    Expression<int>? id,
+    Expression<int>? channelId,
+    Expression<int>? accountId,
+    Expression<String>? content,
+    Expression<DateTime>? createdAt,
+    Expression<DateTime>? cacheExpiredAt,
+  }) {
+    return RawValuesInsertable({
+      if (id != null) 'id': id,
+      if (channelId != null) 'channel_id': channelId,
+      if (accountId != null) 'account_id': accountId,
+      if (content != null) 'content': content,
+      if (createdAt != null) 'created_at': createdAt,
+      if (cacheExpiredAt != null) 'cache_expired_at': cacheExpiredAt,
+    });
+  }
+
+  SnLocalChannelMemberCompanion copyWith(
+      {Value<int>? id,
+      Value<int>? channelId,
+      Value<int>? accountId,
+      Value<String>? content,
+      Value<DateTime>? createdAt,
+      Value<DateTime>? cacheExpiredAt}) {
+    return SnLocalChannelMemberCompanion(
+      id: id ?? this.id,
+      channelId: channelId ?? this.channelId,
+      accountId: accountId ?? this.accountId,
+      content: content ?? this.content,
+      createdAt: createdAt ?? this.createdAt,
+      cacheExpiredAt: cacheExpiredAt ?? this.cacheExpiredAt,
+    );
+  }
+
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    if (id.present) {
+      map['id'] = Variable<int>(id.value);
+    }
+    if (channelId.present) {
+      map['channel_id'] = Variable<int>(channelId.value);
+    }
+    if (accountId.present) {
+      map['account_id'] = Variable<int>(accountId.value);
+    }
+    if (content.present) {
+      map['content'] = Variable<String>(content.value);
+    }
+    if (createdAt.present) {
+      map['created_at'] = Variable<DateTime>(createdAt.value);
+    }
+    if (cacheExpiredAt.present) {
+      map['cache_expired_at'] = Variable<DateTime>(cacheExpiredAt.value);
+    }
+    return map;
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalChannelMemberCompanion(')
+          ..write('id: $id, ')
+          ..write('channelId: $channelId, ')
+          ..write('accountId: $accountId, ')
+          ..write('content: $content, ')
+          ..write('createdAt: $createdAt, ')
+          ..write('cacheExpiredAt: $cacheExpiredAt')
+          ..write(')'))
+        .toString();
+  }
+}
+
+class SnLocalKeyPair extends Table
+    with TableInfo<SnLocalKeyPair, SnLocalKeyPairData> {
+  @override
+  final GeneratedDatabase attachedDatabase;
+  final String? _alias;
+  SnLocalKeyPair(this.attachedDatabase, [this._alias]);
+  late final GeneratedColumn<String> id = GeneratedColumn<String>(
+      'id', aliasedName, false,
+      type: DriftSqlType.string, requiredDuringInsert: true);
+  late final GeneratedColumn<int> accountId = GeneratedColumn<int>(
+      'account_id', aliasedName, false,
+      type: DriftSqlType.int, requiredDuringInsert: true);
+  late final GeneratedColumn<String> publicKey = GeneratedColumn<String>(
+      'public_key', aliasedName, false,
+      type: DriftSqlType.string, requiredDuringInsert: true);
+  late final GeneratedColumn<String> privateKey = GeneratedColumn<String>(
+      'private_key', aliasedName, true,
+      type: DriftSqlType.string, requiredDuringInsert: false);
+  late final GeneratedColumn<bool> isActive = GeneratedColumn<bool>(
+      'is_active', aliasedName, false,
+      type: DriftSqlType.bool,
+      requiredDuringInsert: false,
+      defaultConstraints:
+          GeneratedColumn.constraintIsAlways('CHECK ("is_active" IN (0, 1))'),
+      defaultValue: const CustomExpression('0'));
+  @override
+  List<GeneratedColumn> get $columns =>
+      [id, accountId, publicKey, privateKey, isActive];
+  @override
+  String get aliasedName => _alias ?? actualTableName;
+  @override
+  String get actualTableName => $name;
+  static const String $name = 'sn_local_key_pair';
+  @override
+  Set<GeneratedColumn> get $primaryKey => {id};
+  @override
+  SnLocalKeyPairData map(Map<String, dynamic> data, {String? tablePrefix}) {
+    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+    return SnLocalKeyPairData(
+      id: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}id'])!,
+      accountId: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}account_id'])!,
+      publicKey: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}public_key'])!,
+      privateKey: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}private_key']),
+      isActive: attachedDatabase.typeMapping
+          .read(DriftSqlType.bool, data['${effectivePrefix}is_active'])!,
+    );
+  }
+
+  @override
+  SnLocalKeyPair createAlias(String alias) {
+    return SnLocalKeyPair(attachedDatabase, alias);
+  }
+}
+
+class SnLocalKeyPairData extends DataClass
+    implements Insertable<SnLocalKeyPairData> {
+  final String id;
+  final int accountId;
+  final String publicKey;
+  final String? privateKey;
+  final bool isActive;
+  const SnLocalKeyPairData(
+      {required this.id,
+      required this.accountId,
+      required this.publicKey,
+      this.privateKey,
+      required this.isActive});
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    map['id'] = Variable<String>(id);
+    map['account_id'] = Variable<int>(accountId);
+    map['public_key'] = Variable<String>(publicKey);
+    if (!nullToAbsent || privateKey != null) {
+      map['private_key'] = Variable<String>(privateKey);
+    }
+    map['is_active'] = Variable<bool>(isActive);
+    return map;
+  }
+
+  SnLocalKeyPairCompanion toCompanion(bool nullToAbsent) {
+    return SnLocalKeyPairCompanion(
+      id: Value(id),
+      accountId: Value(accountId),
+      publicKey: Value(publicKey),
+      privateKey: privateKey == null && nullToAbsent
+          ? const Value.absent()
+          : Value(privateKey),
+      isActive: Value(isActive),
+    );
+  }
+
+  factory SnLocalKeyPairData.fromJson(Map<String, dynamic> json,
+      {ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return SnLocalKeyPairData(
+      id: serializer.fromJson<String>(json['id']),
+      accountId: serializer.fromJson<int>(json['accountId']),
+      publicKey: serializer.fromJson<String>(json['publicKey']),
+      privateKey: serializer.fromJson<String?>(json['privateKey']),
+      isActive: serializer.fromJson<bool>(json['isActive']),
+    );
+  }
+  @override
+  Map<String, dynamic> toJson({ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return <String, dynamic>{
+      'id': serializer.toJson<String>(id),
+      'accountId': serializer.toJson<int>(accountId),
+      'publicKey': serializer.toJson<String>(publicKey),
+      'privateKey': serializer.toJson<String?>(privateKey),
+      'isActive': serializer.toJson<bool>(isActive),
+    };
+  }
+
+  SnLocalKeyPairData copyWith(
+          {String? id,
+          int? accountId,
+          String? publicKey,
+          Value<String?> privateKey = const Value.absent(),
+          bool? isActive}) =>
+      SnLocalKeyPairData(
+        id: id ?? this.id,
+        accountId: accountId ?? this.accountId,
+        publicKey: publicKey ?? this.publicKey,
+        privateKey: privateKey.present ? privateKey.value : this.privateKey,
+        isActive: isActive ?? this.isActive,
+      );
+  SnLocalKeyPairData copyWithCompanion(SnLocalKeyPairCompanion data) {
+    return SnLocalKeyPairData(
+      id: data.id.present ? data.id.value : this.id,
+      accountId: data.accountId.present ? data.accountId.value : this.accountId,
+      publicKey: data.publicKey.present ? data.publicKey.value : this.publicKey,
+      privateKey:
+          data.privateKey.present ? data.privateKey.value : this.privateKey,
+      isActive: data.isActive.present ? data.isActive.value : this.isActive,
+    );
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalKeyPairData(')
+          ..write('id: $id, ')
+          ..write('accountId: $accountId, ')
+          ..write('publicKey: $publicKey, ')
+          ..write('privateKey: $privateKey, ')
+          ..write('isActive: $isActive')
+          ..write(')'))
+        .toString();
+  }
+
+  @override
+  int get hashCode =>
+      Object.hash(id, accountId, publicKey, privateKey, isActive);
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      (other is SnLocalKeyPairData &&
+          other.id == this.id &&
+          other.accountId == this.accountId &&
+          other.publicKey == this.publicKey &&
+          other.privateKey == this.privateKey &&
+          other.isActive == this.isActive);
+}
+
+class SnLocalKeyPairCompanion extends UpdateCompanion<SnLocalKeyPairData> {
+  final Value<String> id;
+  final Value<int> accountId;
+  final Value<String> publicKey;
+  final Value<String?> privateKey;
+  final Value<bool> isActive;
+  final Value<int> rowid;
+  const SnLocalKeyPairCompanion({
+    this.id = const Value.absent(),
+    this.accountId = const Value.absent(),
+    this.publicKey = const Value.absent(),
+    this.privateKey = const Value.absent(),
+    this.isActive = const Value.absent(),
+    this.rowid = const Value.absent(),
+  });
+  SnLocalKeyPairCompanion.insert({
+    required String id,
+    required int accountId,
+    required String publicKey,
+    this.privateKey = const Value.absent(),
+    this.isActive = const Value.absent(),
+    this.rowid = const Value.absent(),
+  })  : id = Value(id),
+        accountId = Value(accountId),
+        publicKey = Value(publicKey);
+  static Insertable<SnLocalKeyPairData> custom({
+    Expression<String>? id,
+    Expression<int>? accountId,
+    Expression<String>? publicKey,
+    Expression<String>? privateKey,
+    Expression<bool>? isActive,
+    Expression<int>? rowid,
+  }) {
+    return RawValuesInsertable({
+      if (id != null) 'id': id,
+      if (accountId != null) 'account_id': accountId,
+      if (publicKey != null) 'public_key': publicKey,
+      if (privateKey != null) 'private_key': privateKey,
+      if (isActive != null) 'is_active': isActive,
+      if (rowid != null) 'rowid': rowid,
+    });
+  }
+
+  SnLocalKeyPairCompanion copyWith(
+      {Value<String>? id,
+      Value<int>? accountId,
+      Value<String>? publicKey,
+      Value<String?>? privateKey,
+      Value<bool>? isActive,
+      Value<int>? rowid}) {
+    return SnLocalKeyPairCompanion(
+      id: id ?? this.id,
+      accountId: accountId ?? this.accountId,
+      publicKey: publicKey ?? this.publicKey,
+      privateKey: privateKey ?? this.privateKey,
+      isActive: isActive ?? this.isActive,
+      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 (accountId.present) {
+      map['account_id'] = Variable<int>(accountId.value);
+    }
+    if (publicKey.present) {
+      map['public_key'] = Variable<String>(publicKey.value);
+    }
+    if (privateKey.present) {
+      map['private_key'] = Variable<String>(privateKey.value);
+    }
+    if (isActive.present) {
+      map['is_active'] = Variable<bool>(isActive.value);
+    }
+    if (rowid.present) {
+      map['rowid'] = Variable<int>(rowid.value);
+    }
+    return map;
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalKeyPairCompanion(')
+          ..write('id: $id, ')
+          ..write('accountId: $accountId, ')
+          ..write('publicKey: $publicKey, ')
+          ..write('privateKey: $privateKey, ')
+          ..write('isActive: $isActive, ')
+          ..write('rowid: $rowid')
+          ..write(')'))
+        .toString();
+  }
+}
+
+class SnLocalAccount extends Table
+    with TableInfo<SnLocalAccount, SnLocalAccountData> {
+  @override
+  final GeneratedDatabase attachedDatabase;
+  final String? _alias;
+  SnLocalAccount(this.attachedDatabase, [this._alias]);
+  late final GeneratedColumn<int> id = GeneratedColumn<int>(
+      'id', aliasedName, false,
+      hasAutoIncrement: true,
+      type: DriftSqlType.int,
+      requiredDuringInsert: false,
+      defaultConstraints:
+          GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
+  late final GeneratedColumn<String> name = GeneratedColumn<String>(
+      'name', aliasedName, false,
+      type: DriftSqlType.string, requiredDuringInsert: true);
+  late final GeneratedColumn<String> content = GeneratedColumn<String>(
+      'content', aliasedName, false,
+      type: DriftSqlType.string, requiredDuringInsert: true);
+  late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
+      'created_at', aliasedName, false,
+      type: DriftSqlType.dateTime,
+      requiredDuringInsert: false,
+      defaultValue: const CustomExpression(
+          'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)'));
+  late final GeneratedColumn<DateTime> cacheExpiredAt =
+      GeneratedColumn<DateTime>('cache_expired_at', aliasedName, false,
+          type: DriftSqlType.dateTime, requiredDuringInsert: true);
+  @override
+  List<GeneratedColumn> get $columns =>
+      [id, name, content, createdAt, cacheExpiredAt];
+  @override
+  String get aliasedName => _alias ?? actualTableName;
+  @override
+  String get actualTableName => $name;
+  static const String $name = 'sn_local_account';
+  @override
+  Set<GeneratedColumn> get $primaryKey => {id};
+  @override
+  SnLocalAccountData map(Map<String, dynamic> data, {String? tablePrefix}) {
+    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+    return SnLocalAccountData(
+      id: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}id'])!,
+      name: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}name'])!,
+      content: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}content'])!,
+      createdAt: attachedDatabase.typeMapping
+          .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
+      cacheExpiredAt: attachedDatabase.typeMapping.read(
+          DriftSqlType.dateTime, data['${effectivePrefix}cache_expired_at'])!,
+    );
+  }
+
+  @override
+  SnLocalAccount createAlias(String alias) {
+    return SnLocalAccount(attachedDatabase, alias);
+  }
+}
+
+class SnLocalAccountData extends DataClass
+    implements Insertable<SnLocalAccountData> {
+  final int id;
+  final String name;
+  final String content;
+  final DateTime createdAt;
+  final DateTime cacheExpiredAt;
+  const SnLocalAccountData(
+      {required this.id,
+      required this.name,
+      required this.content,
+      required this.createdAt,
+      required this.cacheExpiredAt});
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    map['id'] = Variable<int>(id);
+    map['name'] = Variable<String>(name);
+    map['content'] = Variable<String>(content);
+    map['created_at'] = Variable<DateTime>(createdAt);
+    map['cache_expired_at'] = Variable<DateTime>(cacheExpiredAt);
+    return map;
+  }
+
+  SnLocalAccountCompanion toCompanion(bool nullToAbsent) {
+    return SnLocalAccountCompanion(
+      id: Value(id),
+      name: Value(name),
+      content: Value(content),
+      createdAt: Value(createdAt),
+      cacheExpiredAt: Value(cacheExpiredAt),
+    );
+  }
+
+  factory SnLocalAccountData.fromJson(Map<String, dynamic> json,
+      {ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return SnLocalAccountData(
+      id: serializer.fromJson<int>(json['id']),
+      name: serializer.fromJson<String>(json['name']),
+      content: serializer.fromJson<String>(json['content']),
+      createdAt: serializer.fromJson<DateTime>(json['createdAt']),
+      cacheExpiredAt: serializer.fromJson<DateTime>(json['cacheExpiredAt']),
+    );
+  }
+  @override
+  Map<String, dynamic> toJson({ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return <String, dynamic>{
+      'id': serializer.toJson<int>(id),
+      'name': serializer.toJson<String>(name),
+      'content': serializer.toJson<String>(content),
+      'createdAt': serializer.toJson<DateTime>(createdAt),
+      'cacheExpiredAt': serializer.toJson<DateTime>(cacheExpiredAt),
+    };
+  }
+
+  SnLocalAccountData copyWith(
+          {int? id,
+          String? name,
+          String? content,
+          DateTime? createdAt,
+          DateTime? cacheExpiredAt}) =>
+      SnLocalAccountData(
+        id: id ?? this.id,
+        name: name ?? this.name,
+        content: content ?? this.content,
+        createdAt: createdAt ?? this.createdAt,
+        cacheExpiredAt: cacheExpiredAt ?? this.cacheExpiredAt,
+      );
+  SnLocalAccountData copyWithCompanion(SnLocalAccountCompanion data) {
+    return SnLocalAccountData(
+      id: data.id.present ? data.id.value : this.id,
+      name: data.name.present ? data.name.value : this.name,
+      content: data.content.present ? data.content.value : this.content,
+      createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
+      cacheExpiredAt: data.cacheExpiredAt.present
+          ? data.cacheExpiredAt.value
+          : this.cacheExpiredAt,
+    );
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalAccountData(')
+          ..write('id: $id, ')
+          ..write('name: $name, ')
+          ..write('content: $content, ')
+          ..write('createdAt: $createdAt, ')
+          ..write('cacheExpiredAt: $cacheExpiredAt')
+          ..write(')'))
+        .toString();
+  }
+
+  @override
+  int get hashCode => Object.hash(id, name, content, createdAt, cacheExpiredAt);
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      (other is SnLocalAccountData &&
+          other.id == this.id &&
+          other.name == this.name &&
+          other.content == this.content &&
+          other.createdAt == this.createdAt &&
+          other.cacheExpiredAt == this.cacheExpiredAt);
+}
+
+class SnLocalAccountCompanion extends UpdateCompanion<SnLocalAccountData> {
+  final Value<int> id;
+  final Value<String> name;
+  final Value<String> content;
+  final Value<DateTime> createdAt;
+  final Value<DateTime> cacheExpiredAt;
+  const SnLocalAccountCompanion({
+    this.id = const Value.absent(),
+    this.name = const Value.absent(),
+    this.content = const Value.absent(),
+    this.createdAt = const Value.absent(),
+    this.cacheExpiredAt = const Value.absent(),
+  });
+  SnLocalAccountCompanion.insert({
+    this.id = const Value.absent(),
+    required String name,
+    required String content,
+    this.createdAt = const Value.absent(),
+    required DateTime cacheExpiredAt,
+  })  : name = Value(name),
+        content = Value(content),
+        cacheExpiredAt = Value(cacheExpiredAt);
+  static Insertable<SnLocalAccountData> custom({
+    Expression<int>? id,
+    Expression<String>? name,
+    Expression<String>? content,
+    Expression<DateTime>? createdAt,
+    Expression<DateTime>? cacheExpiredAt,
+  }) {
+    return RawValuesInsertable({
+      if (id != null) 'id': id,
+      if (name != null) 'name': name,
+      if (content != null) 'content': content,
+      if (createdAt != null) 'created_at': createdAt,
+      if (cacheExpiredAt != null) 'cache_expired_at': cacheExpiredAt,
+    });
+  }
+
+  SnLocalAccountCompanion copyWith(
+      {Value<int>? id,
+      Value<String>? name,
+      Value<String>? content,
+      Value<DateTime>? createdAt,
+      Value<DateTime>? cacheExpiredAt}) {
+    return SnLocalAccountCompanion(
+      id: id ?? this.id,
+      name: name ?? this.name,
+      content: content ?? this.content,
+      createdAt: createdAt ?? this.createdAt,
+      cacheExpiredAt: cacheExpiredAt ?? this.cacheExpiredAt,
+    );
+  }
+
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    if (id.present) {
+      map['id'] = Variable<int>(id.value);
+    }
+    if (name.present) {
+      map['name'] = Variable<String>(name.value);
+    }
+    if (content.present) {
+      map['content'] = Variable<String>(content.value);
+    }
+    if (createdAt.present) {
+      map['created_at'] = Variable<DateTime>(createdAt.value);
+    }
+    if (cacheExpiredAt.present) {
+      map['cache_expired_at'] = Variable<DateTime>(cacheExpiredAt.value);
+    }
+    return map;
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalAccountCompanion(')
+          ..write('id: $id, ')
+          ..write('name: $name, ')
+          ..write('content: $content, ')
+          ..write('createdAt: $createdAt, ')
+          ..write('cacheExpiredAt: $cacheExpiredAt')
+          ..write(')'))
+        .toString();
+  }
+}
+
+class SnLocalAttachment extends Table
+    with TableInfo<SnLocalAttachment, SnLocalAttachmentData> {
+  @override
+  final GeneratedDatabase attachedDatabase;
+  final String? _alias;
+  SnLocalAttachment(this.attachedDatabase, [this._alias]);
+  late final GeneratedColumn<int> id = GeneratedColumn<int>(
+      'id', aliasedName, false,
+      hasAutoIncrement: true,
+      type: DriftSqlType.int,
+      requiredDuringInsert: false,
+      defaultConstraints:
+          GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
+  late final GeneratedColumn<String> rid = GeneratedColumn<String>(
+      'rid', aliasedName, false,
+      type: DriftSqlType.string,
+      requiredDuringInsert: true,
+      defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE'));
+  late final GeneratedColumn<String> uuid = GeneratedColumn<String>(
+      'uuid', aliasedName, false,
+      type: DriftSqlType.string,
+      requiredDuringInsert: true,
+      defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE'));
+  late final GeneratedColumn<String> content = GeneratedColumn<String>(
+      'content', aliasedName, false,
+      type: DriftSqlType.string, requiredDuringInsert: true);
+  late final GeneratedColumn<int> accountId = GeneratedColumn<int>(
+      'account_id', aliasedName, false,
+      type: DriftSqlType.int, requiredDuringInsert: true);
+  late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
+      'created_at', aliasedName, false,
+      type: DriftSqlType.dateTime,
+      requiredDuringInsert: false,
+      defaultValue: const CustomExpression(
+          'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)'));
+  late final GeneratedColumn<DateTime> cacheExpiredAt =
+      GeneratedColumn<DateTime>('cache_expired_at', aliasedName, false,
+          type: DriftSqlType.dateTime, requiredDuringInsert: true);
+  @override
+  List<GeneratedColumn> get $columns =>
+      [id, rid, uuid, content, accountId, createdAt, cacheExpiredAt];
+  @override
+  String get aliasedName => _alias ?? actualTableName;
+  @override
+  String get actualTableName => $name;
+  static const String $name = 'sn_local_attachment';
+  @override
+  Set<GeneratedColumn> get $primaryKey => {id};
+  @override
+  SnLocalAttachmentData map(Map<String, dynamic> data, {String? tablePrefix}) {
+    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+    return SnLocalAttachmentData(
+      id: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}id'])!,
+      rid: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}rid'])!,
+      uuid: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}uuid'])!,
+      content: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}content'])!,
+      accountId: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}account_id'])!,
+      createdAt: attachedDatabase.typeMapping
+          .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
+      cacheExpiredAt: attachedDatabase.typeMapping.read(
+          DriftSqlType.dateTime, data['${effectivePrefix}cache_expired_at'])!,
+    );
+  }
+
+  @override
+  SnLocalAttachment createAlias(String alias) {
+    return SnLocalAttachment(attachedDatabase, alias);
+  }
+}
+
+class SnLocalAttachmentData extends DataClass
+    implements Insertable<SnLocalAttachmentData> {
+  final int id;
+  final String rid;
+  final String uuid;
+  final String content;
+  final int accountId;
+  final DateTime createdAt;
+  final DateTime cacheExpiredAt;
+  const SnLocalAttachmentData(
+      {required this.id,
+      required this.rid,
+      required this.uuid,
+      required this.content,
+      required this.accountId,
+      required this.createdAt,
+      required this.cacheExpiredAt});
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    map['id'] = Variable<int>(id);
+    map['rid'] = Variable<String>(rid);
+    map['uuid'] = Variable<String>(uuid);
+    map['content'] = Variable<String>(content);
+    map['account_id'] = Variable<int>(accountId);
+    map['created_at'] = Variable<DateTime>(createdAt);
+    map['cache_expired_at'] = Variable<DateTime>(cacheExpiredAt);
+    return map;
+  }
+
+  SnLocalAttachmentCompanion toCompanion(bool nullToAbsent) {
+    return SnLocalAttachmentCompanion(
+      id: Value(id),
+      rid: Value(rid),
+      uuid: Value(uuid),
+      content: Value(content),
+      accountId: Value(accountId),
+      createdAt: Value(createdAt),
+      cacheExpiredAt: Value(cacheExpiredAt),
+    );
+  }
+
+  factory SnLocalAttachmentData.fromJson(Map<String, dynamic> json,
+      {ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return SnLocalAttachmentData(
+      id: serializer.fromJson<int>(json['id']),
+      rid: serializer.fromJson<String>(json['rid']),
+      uuid: serializer.fromJson<String>(json['uuid']),
+      content: serializer.fromJson<String>(json['content']),
+      accountId: serializer.fromJson<int>(json['accountId']),
+      createdAt: serializer.fromJson<DateTime>(json['createdAt']),
+      cacheExpiredAt: serializer.fromJson<DateTime>(json['cacheExpiredAt']),
+    );
+  }
+  @override
+  Map<String, dynamic> toJson({ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return <String, dynamic>{
+      'id': serializer.toJson<int>(id),
+      'rid': serializer.toJson<String>(rid),
+      'uuid': serializer.toJson<String>(uuid),
+      'content': serializer.toJson<String>(content),
+      'accountId': serializer.toJson<int>(accountId),
+      'createdAt': serializer.toJson<DateTime>(createdAt),
+      'cacheExpiredAt': serializer.toJson<DateTime>(cacheExpiredAt),
+    };
+  }
+
+  SnLocalAttachmentData copyWith(
+          {int? id,
+          String? rid,
+          String? uuid,
+          String? content,
+          int? accountId,
+          DateTime? createdAt,
+          DateTime? cacheExpiredAt}) =>
+      SnLocalAttachmentData(
+        id: id ?? this.id,
+        rid: rid ?? this.rid,
+        uuid: uuid ?? this.uuid,
+        content: content ?? this.content,
+        accountId: accountId ?? this.accountId,
+        createdAt: createdAt ?? this.createdAt,
+        cacheExpiredAt: cacheExpiredAt ?? this.cacheExpiredAt,
+      );
+  SnLocalAttachmentData copyWithCompanion(SnLocalAttachmentCompanion data) {
+    return SnLocalAttachmentData(
+      id: data.id.present ? data.id.value : this.id,
+      rid: data.rid.present ? data.rid.value : this.rid,
+      uuid: data.uuid.present ? data.uuid.value : this.uuid,
+      content: data.content.present ? data.content.value : this.content,
+      accountId: data.accountId.present ? data.accountId.value : this.accountId,
+      createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
+      cacheExpiredAt: data.cacheExpiredAt.present
+          ? data.cacheExpiredAt.value
+          : this.cacheExpiredAt,
+    );
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalAttachmentData(')
+          ..write('id: $id, ')
+          ..write('rid: $rid, ')
+          ..write('uuid: $uuid, ')
+          ..write('content: $content, ')
+          ..write('accountId: $accountId, ')
+          ..write('createdAt: $createdAt, ')
+          ..write('cacheExpiredAt: $cacheExpiredAt')
+          ..write(')'))
+        .toString();
+  }
+
+  @override
+  int get hashCode =>
+      Object.hash(id, rid, uuid, content, accountId, createdAt, cacheExpiredAt);
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      (other is SnLocalAttachmentData &&
+          other.id == this.id &&
+          other.rid == this.rid &&
+          other.uuid == this.uuid &&
+          other.content == this.content &&
+          other.accountId == this.accountId &&
+          other.createdAt == this.createdAt &&
+          other.cacheExpiredAt == this.cacheExpiredAt);
+}
+
+class SnLocalAttachmentCompanion
+    extends UpdateCompanion<SnLocalAttachmentData> {
+  final Value<int> id;
+  final Value<String> rid;
+  final Value<String> uuid;
+  final Value<String> content;
+  final Value<int> accountId;
+  final Value<DateTime> createdAt;
+  final Value<DateTime> cacheExpiredAt;
+  const SnLocalAttachmentCompanion({
+    this.id = const Value.absent(),
+    this.rid = const Value.absent(),
+    this.uuid = const Value.absent(),
+    this.content = const Value.absent(),
+    this.accountId = const Value.absent(),
+    this.createdAt = const Value.absent(),
+    this.cacheExpiredAt = const Value.absent(),
+  });
+  SnLocalAttachmentCompanion.insert({
+    this.id = const Value.absent(),
+    required String rid,
+    required String uuid,
+    required String content,
+    required int accountId,
+    this.createdAt = const Value.absent(),
+    required DateTime cacheExpiredAt,
+  })  : rid = Value(rid),
+        uuid = Value(uuid),
+        content = Value(content),
+        accountId = Value(accountId),
+        cacheExpiredAt = Value(cacheExpiredAt);
+  static Insertable<SnLocalAttachmentData> custom({
+    Expression<int>? id,
+    Expression<String>? rid,
+    Expression<String>? uuid,
+    Expression<String>? content,
+    Expression<int>? accountId,
+    Expression<DateTime>? createdAt,
+    Expression<DateTime>? cacheExpiredAt,
+  }) {
+    return RawValuesInsertable({
+      if (id != null) 'id': id,
+      if (rid != null) 'rid': rid,
+      if (uuid != null) 'uuid': uuid,
+      if (content != null) 'content': content,
+      if (accountId != null) 'account_id': accountId,
+      if (createdAt != null) 'created_at': createdAt,
+      if (cacheExpiredAt != null) 'cache_expired_at': cacheExpiredAt,
+    });
+  }
+
+  SnLocalAttachmentCompanion copyWith(
+      {Value<int>? id,
+      Value<String>? rid,
+      Value<String>? uuid,
+      Value<String>? content,
+      Value<int>? accountId,
+      Value<DateTime>? createdAt,
+      Value<DateTime>? cacheExpiredAt}) {
+    return SnLocalAttachmentCompanion(
+      id: id ?? this.id,
+      rid: rid ?? this.rid,
+      uuid: uuid ?? this.uuid,
+      content: content ?? this.content,
+      accountId: accountId ?? this.accountId,
+      createdAt: createdAt ?? this.createdAt,
+      cacheExpiredAt: cacheExpiredAt ?? this.cacheExpiredAt,
+    );
+  }
+
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    if (id.present) {
+      map['id'] = Variable<int>(id.value);
+    }
+    if (rid.present) {
+      map['rid'] = Variable<String>(rid.value);
+    }
+    if (uuid.present) {
+      map['uuid'] = Variable<String>(uuid.value);
+    }
+    if (content.present) {
+      map['content'] = Variable<String>(content.value);
+    }
+    if (accountId.present) {
+      map['account_id'] = Variable<int>(accountId.value);
+    }
+    if (createdAt.present) {
+      map['created_at'] = Variable<DateTime>(createdAt.value);
+    }
+    if (cacheExpiredAt.present) {
+      map['cache_expired_at'] = Variable<DateTime>(cacheExpiredAt.value);
+    }
+    return map;
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalAttachmentCompanion(')
+          ..write('id: $id, ')
+          ..write('rid: $rid, ')
+          ..write('uuid: $uuid, ')
+          ..write('content: $content, ')
+          ..write('accountId: $accountId, ')
+          ..write('createdAt: $createdAt, ')
+          ..write('cacheExpiredAt: $cacheExpiredAt')
+          ..write(')'))
+        .toString();
+  }
+}
+
+class SnLocalSticker extends Table
+    with TableInfo<SnLocalSticker, SnLocalStickerData> {
+  @override
+  final GeneratedDatabase attachedDatabase;
+  final String? _alias;
+  SnLocalSticker(this.attachedDatabase, [this._alias]);
+  late final GeneratedColumn<int> id = GeneratedColumn<int>(
+      'id', aliasedName, false,
+      hasAutoIncrement: true,
+      type: DriftSqlType.int,
+      requiredDuringInsert: false,
+      defaultConstraints:
+          GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
+  late final GeneratedColumn<String> alias = GeneratedColumn<String>(
+      'alias', aliasedName, false,
+      type: DriftSqlType.string, requiredDuringInsert: true);
+  late final GeneratedColumn<String> fullAlias = GeneratedColumn<String>(
+      'full_alias', aliasedName, false,
+      type: DriftSqlType.string, requiredDuringInsert: true);
+  late final GeneratedColumn<String> content = GeneratedColumn<String>(
+      'content', aliasedName, false,
+      type: DriftSqlType.string, requiredDuringInsert: true);
+  late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
+      'created_at', aliasedName, false,
+      type: DriftSqlType.dateTime,
+      requiredDuringInsert: false,
+      defaultValue: const CustomExpression(
+          'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)'));
+  @override
+  List<GeneratedColumn> get $columns =>
+      [id, alias, fullAlias, content, createdAt];
+  @override
+  String get aliasedName => _alias ?? actualTableName;
+  @override
+  String get actualTableName => $name;
+  static const String $name = 'sn_local_sticker';
+  @override
+  Set<GeneratedColumn> get $primaryKey => {id};
+  @override
+  SnLocalStickerData map(Map<String, dynamic> data, {String? tablePrefix}) {
+    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+    return SnLocalStickerData(
+      id: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}id'])!,
+      alias: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}alias'])!,
+      fullAlias: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}full_alias'])!,
+      content: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}content'])!,
+      createdAt: attachedDatabase.typeMapping
+          .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
+    );
+  }
+
+  @override
+  SnLocalSticker createAlias(String alias) {
+    return SnLocalSticker(attachedDatabase, alias);
+  }
+}
+
+class SnLocalStickerData extends DataClass
+    implements Insertable<SnLocalStickerData> {
+  final int id;
+  final String alias;
+  final String fullAlias;
+  final String content;
+  final DateTime createdAt;
+  const SnLocalStickerData(
+      {required this.id,
+      required this.alias,
+      required this.fullAlias,
+      required this.content,
+      required this.createdAt});
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    map['id'] = Variable<int>(id);
+    map['alias'] = Variable<String>(alias);
+    map['full_alias'] = Variable<String>(fullAlias);
+    map['content'] = Variable<String>(content);
+    map['created_at'] = Variable<DateTime>(createdAt);
+    return map;
+  }
+
+  SnLocalStickerCompanion toCompanion(bool nullToAbsent) {
+    return SnLocalStickerCompanion(
+      id: Value(id),
+      alias: Value(alias),
+      fullAlias: Value(fullAlias),
+      content: Value(content),
+      createdAt: Value(createdAt),
+    );
+  }
+
+  factory SnLocalStickerData.fromJson(Map<String, dynamic> json,
+      {ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return SnLocalStickerData(
+      id: serializer.fromJson<int>(json['id']),
+      alias: serializer.fromJson<String>(json['alias']),
+      fullAlias: serializer.fromJson<String>(json['fullAlias']),
+      content: serializer.fromJson<String>(json['content']),
+      createdAt: serializer.fromJson<DateTime>(json['createdAt']),
+    );
+  }
+  @override
+  Map<String, dynamic> toJson({ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return <String, dynamic>{
+      'id': serializer.toJson<int>(id),
+      'alias': serializer.toJson<String>(alias),
+      'fullAlias': serializer.toJson<String>(fullAlias),
+      'content': serializer.toJson<String>(content),
+      'createdAt': serializer.toJson<DateTime>(createdAt),
+    };
+  }
+
+  SnLocalStickerData copyWith(
+          {int? id,
+          String? alias,
+          String? fullAlias,
+          String? content,
+          DateTime? createdAt}) =>
+      SnLocalStickerData(
+        id: id ?? this.id,
+        alias: alias ?? this.alias,
+        fullAlias: fullAlias ?? this.fullAlias,
+        content: content ?? this.content,
+        createdAt: createdAt ?? this.createdAt,
+      );
+  SnLocalStickerData copyWithCompanion(SnLocalStickerCompanion data) {
+    return SnLocalStickerData(
+      id: data.id.present ? data.id.value : this.id,
+      alias: data.alias.present ? data.alias.value : this.alias,
+      fullAlias: data.fullAlias.present ? data.fullAlias.value : this.fullAlias,
+      content: data.content.present ? data.content.value : this.content,
+      createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
+    );
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalStickerData(')
+          ..write('id: $id, ')
+          ..write('alias: $alias, ')
+          ..write('fullAlias: $fullAlias, ')
+          ..write('content: $content, ')
+          ..write('createdAt: $createdAt')
+          ..write(')'))
+        .toString();
+  }
+
+  @override
+  int get hashCode => Object.hash(id, alias, fullAlias, content, createdAt);
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      (other is SnLocalStickerData &&
+          other.id == this.id &&
+          other.alias == this.alias &&
+          other.fullAlias == this.fullAlias &&
+          other.content == this.content &&
+          other.createdAt == this.createdAt);
+}
+
+class SnLocalStickerCompanion extends UpdateCompanion<SnLocalStickerData> {
+  final Value<int> id;
+  final Value<String> alias;
+  final Value<String> fullAlias;
+  final Value<String> content;
+  final Value<DateTime> createdAt;
+  const SnLocalStickerCompanion({
+    this.id = const Value.absent(),
+    this.alias = const Value.absent(),
+    this.fullAlias = const Value.absent(),
+    this.content = const Value.absent(),
+    this.createdAt = const Value.absent(),
+  });
+  SnLocalStickerCompanion.insert({
+    this.id = const Value.absent(),
+    required String alias,
+    required String fullAlias,
+    required String content,
+    this.createdAt = const Value.absent(),
+  })  : alias = Value(alias),
+        fullAlias = Value(fullAlias),
+        content = Value(content);
+  static Insertable<SnLocalStickerData> custom({
+    Expression<int>? id,
+    Expression<String>? alias,
+    Expression<String>? fullAlias,
+    Expression<String>? content,
+    Expression<DateTime>? createdAt,
+  }) {
+    return RawValuesInsertable({
+      if (id != null) 'id': id,
+      if (alias != null) 'alias': alias,
+      if (fullAlias != null) 'full_alias': fullAlias,
+      if (content != null) 'content': content,
+      if (createdAt != null) 'created_at': createdAt,
+    });
+  }
+
+  SnLocalStickerCompanion copyWith(
+      {Value<int>? id,
+      Value<String>? alias,
+      Value<String>? fullAlias,
+      Value<String>? content,
+      Value<DateTime>? createdAt}) {
+    return SnLocalStickerCompanion(
+      id: id ?? this.id,
+      alias: alias ?? this.alias,
+      fullAlias: fullAlias ?? this.fullAlias,
+      content: content ?? this.content,
+      createdAt: createdAt ?? this.createdAt,
+    );
+  }
+
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    if (id.present) {
+      map['id'] = Variable<int>(id.value);
+    }
+    if (alias.present) {
+      map['alias'] = Variable<String>(alias.value);
+    }
+    if (fullAlias.present) {
+      map['full_alias'] = Variable<String>(fullAlias.value);
+    }
+    if (content.present) {
+      map['content'] = Variable<String>(content.value);
+    }
+    if (createdAt.present) {
+      map['created_at'] = Variable<DateTime>(createdAt.value);
+    }
+    return map;
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalStickerCompanion(')
+          ..write('id: $id, ')
+          ..write('alias: $alias, ')
+          ..write('fullAlias: $fullAlias, ')
+          ..write('content: $content, ')
+          ..write('createdAt: $createdAt')
+          ..write(')'))
+        .toString();
+  }
+}
+
+class SnLocalStickerPack extends Table
+    with TableInfo<SnLocalStickerPack, SnLocalStickerPackData> {
+  @override
+  final GeneratedDatabase attachedDatabase;
+  final String? _alias;
+  SnLocalStickerPack(this.attachedDatabase, [this._alias]);
+  late final GeneratedColumn<int> id = GeneratedColumn<int>(
+      'id', aliasedName, false,
+      hasAutoIncrement: true,
+      type: DriftSqlType.int,
+      requiredDuringInsert: false,
+      defaultConstraints:
+          GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
+  late final GeneratedColumn<String> content = GeneratedColumn<String>(
+      'content', aliasedName, false,
+      type: DriftSqlType.string, requiredDuringInsert: true);
+  late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
+      'created_at', aliasedName, false,
+      type: DriftSqlType.dateTime,
+      requiredDuringInsert: false,
+      defaultValue: const CustomExpression(
+          'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)'));
+  @override
+  List<GeneratedColumn> get $columns => [id, content, createdAt];
+  @override
+  String get aliasedName => _alias ?? actualTableName;
+  @override
+  String get actualTableName => $name;
+  static const String $name = 'sn_local_sticker_pack';
+  @override
+  Set<GeneratedColumn> get $primaryKey => {id};
+  @override
+  SnLocalStickerPackData map(Map<String, dynamic> data, {String? tablePrefix}) {
+    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+    return SnLocalStickerPackData(
+      id: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}id'])!,
+      content: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}content'])!,
+      createdAt: attachedDatabase.typeMapping
+          .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
+    );
+  }
+
+  @override
+  SnLocalStickerPack createAlias(String alias) {
+    return SnLocalStickerPack(attachedDatabase, alias);
+  }
+}
+
+class SnLocalStickerPackData extends DataClass
+    implements Insertable<SnLocalStickerPackData> {
+  final int id;
+  final String content;
+  final DateTime createdAt;
+  const SnLocalStickerPackData(
+      {required this.id, required this.content, required this.createdAt});
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    map['id'] = Variable<int>(id);
+    map['content'] = Variable<String>(content);
+    map['created_at'] = Variable<DateTime>(createdAt);
+    return map;
+  }
+
+  SnLocalStickerPackCompanion toCompanion(bool nullToAbsent) {
+    return SnLocalStickerPackCompanion(
+      id: Value(id),
+      content: Value(content),
+      createdAt: Value(createdAt),
+    );
+  }
+
+  factory SnLocalStickerPackData.fromJson(Map<String, dynamic> json,
+      {ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return SnLocalStickerPackData(
+      id: serializer.fromJson<int>(json['id']),
+      content: serializer.fromJson<String>(json['content']),
+      createdAt: serializer.fromJson<DateTime>(json['createdAt']),
+    );
+  }
+  @override
+  Map<String, dynamic> toJson({ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return <String, dynamic>{
+      'id': serializer.toJson<int>(id),
+      'content': serializer.toJson<String>(content),
+      'createdAt': serializer.toJson<DateTime>(createdAt),
+    };
+  }
+
+  SnLocalStickerPackData copyWith(
+          {int? id, String? content, DateTime? createdAt}) =>
+      SnLocalStickerPackData(
+        id: id ?? this.id,
+        content: content ?? this.content,
+        createdAt: createdAt ?? this.createdAt,
+      );
+  SnLocalStickerPackData copyWithCompanion(SnLocalStickerPackCompanion data) {
+    return SnLocalStickerPackData(
+      id: data.id.present ? data.id.value : this.id,
+      content: data.content.present ? data.content.value : this.content,
+      createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
+    );
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalStickerPackData(')
+          ..write('id: $id, ')
+          ..write('content: $content, ')
+          ..write('createdAt: $createdAt')
+          ..write(')'))
+        .toString();
+  }
+
+  @override
+  int get hashCode => Object.hash(id, content, createdAt);
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      (other is SnLocalStickerPackData &&
+          other.id == this.id &&
+          other.content == this.content &&
+          other.createdAt == this.createdAt);
+}
+
+class SnLocalStickerPackCompanion
+    extends UpdateCompanion<SnLocalStickerPackData> {
+  final Value<int> id;
+  final Value<String> content;
+  final Value<DateTime> createdAt;
+  const SnLocalStickerPackCompanion({
+    this.id = const Value.absent(),
+    this.content = const Value.absent(),
+    this.createdAt = const Value.absent(),
+  });
+  SnLocalStickerPackCompanion.insert({
+    this.id = const Value.absent(),
+    required String content,
+    this.createdAt = const Value.absent(),
+  }) : content = Value(content);
+  static Insertable<SnLocalStickerPackData> custom({
+    Expression<int>? id,
+    Expression<String>? content,
+    Expression<DateTime>? createdAt,
+  }) {
+    return RawValuesInsertable({
+      if (id != null) 'id': id,
+      if (content != null) 'content': content,
+      if (createdAt != null) 'created_at': createdAt,
+    });
+  }
+
+  SnLocalStickerPackCompanion copyWith(
+      {Value<int>? id, Value<String>? content, Value<DateTime>? createdAt}) {
+    return SnLocalStickerPackCompanion(
+      id: id ?? this.id,
+      content: content ?? this.content,
+      createdAt: createdAt ?? this.createdAt,
+    );
+  }
+
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    if (id.present) {
+      map['id'] = Variable<int>(id.value);
+    }
+    if (content.present) {
+      map['content'] = Variable<String>(content.value);
+    }
+    if (createdAt.present) {
+      map['created_at'] = Variable<DateTime>(createdAt.value);
+    }
+    return map;
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalStickerPackCompanion(')
+          ..write('id: $id, ')
+          ..write('content: $content, ')
+          ..write('createdAt: $createdAt')
+          ..write(')'))
+        .toString();
+  }
+}
+
+class SnLocalRealm extends Table
+    with TableInfo<SnLocalRealm, SnLocalRealmData> {
+  @override
+  final GeneratedDatabase attachedDatabase;
+  final String? _alias;
+  SnLocalRealm(this.attachedDatabase, [this._alias]);
+  late final GeneratedColumn<int> id = GeneratedColumn<int>(
+      'id', aliasedName, false,
+      hasAutoIncrement: true,
+      type: DriftSqlType.int,
+      requiredDuringInsert: false,
+      defaultConstraints:
+          GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
+  late final GeneratedColumn<String> alias = GeneratedColumn<String>(
+      'alias', aliasedName, false,
+      type: DriftSqlType.string,
+      requiredDuringInsert: true,
+      defaultConstraints: GeneratedColumn.constraintIsAlways('UNIQUE'));
+  late final GeneratedColumn<String> content = GeneratedColumn<String>(
+      'content', aliasedName, false,
+      type: DriftSqlType.string, requiredDuringInsert: true);
+  late final GeneratedColumn<int> accountId = GeneratedColumn<int>(
+      'account_id', aliasedName, false,
+      type: DriftSqlType.int, requiredDuringInsert: true);
+  late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
+      'created_at', aliasedName, false,
+      type: DriftSqlType.dateTime,
+      requiredDuringInsert: false,
+      defaultValue: const CustomExpression(
+          'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)'));
+  late final GeneratedColumn<DateTime> cacheExpiredAt =
+      GeneratedColumn<DateTime>('cache_expired_at', aliasedName, false,
+          type: DriftSqlType.dateTime, requiredDuringInsert: true);
+  @override
+  List<GeneratedColumn> get $columns =>
+      [id, alias, content, accountId, createdAt, cacheExpiredAt];
+  @override
+  String get aliasedName => _alias ?? actualTableName;
+  @override
+  String get actualTableName => $name;
+  static const String $name = 'sn_local_realm';
+  @override
+  Set<GeneratedColumn> get $primaryKey => {id};
+  @override
+  SnLocalRealmData map(Map<String, dynamic> data, {String? tablePrefix}) {
+    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+    return SnLocalRealmData(
+      id: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}id'])!,
+      alias: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}alias'])!,
+      content: attachedDatabase.typeMapping
+          .read(DriftSqlType.string, data['${effectivePrefix}content'])!,
+      accountId: attachedDatabase.typeMapping
+          .read(DriftSqlType.int, data['${effectivePrefix}account_id'])!,
+      createdAt: attachedDatabase.typeMapping
+          .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
+      cacheExpiredAt: attachedDatabase.typeMapping.read(
+          DriftSqlType.dateTime, data['${effectivePrefix}cache_expired_at'])!,
+    );
+  }
+
+  @override
+  SnLocalRealm createAlias(String alias) {
+    return SnLocalRealm(attachedDatabase, alias);
+  }
+}
+
+class SnLocalRealmData extends DataClass
+    implements Insertable<SnLocalRealmData> {
+  final int id;
+  final String alias;
+  final String content;
+  final int accountId;
+  final DateTime createdAt;
+  final DateTime cacheExpiredAt;
+  const SnLocalRealmData(
+      {required this.id,
+      required this.alias,
+      required this.content,
+      required this.accountId,
+      required this.createdAt,
+      required this.cacheExpiredAt});
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    map['id'] = Variable<int>(id);
+    map['alias'] = Variable<String>(alias);
+    map['content'] = Variable<String>(content);
+    map['account_id'] = Variable<int>(accountId);
+    map['created_at'] = Variable<DateTime>(createdAt);
+    map['cache_expired_at'] = Variable<DateTime>(cacheExpiredAt);
+    return map;
+  }
+
+  SnLocalRealmCompanion toCompanion(bool nullToAbsent) {
+    return SnLocalRealmCompanion(
+      id: Value(id),
+      alias: Value(alias),
+      content: Value(content),
+      accountId: Value(accountId),
+      createdAt: Value(createdAt),
+      cacheExpiredAt: Value(cacheExpiredAt),
+    );
+  }
+
+  factory SnLocalRealmData.fromJson(Map<String, dynamic> json,
+      {ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return SnLocalRealmData(
+      id: serializer.fromJson<int>(json['id']),
+      alias: serializer.fromJson<String>(json['alias']),
+      content: serializer.fromJson<String>(json['content']),
+      accountId: serializer.fromJson<int>(json['accountId']),
+      createdAt: serializer.fromJson<DateTime>(json['createdAt']),
+      cacheExpiredAt: serializer.fromJson<DateTime>(json['cacheExpiredAt']),
+    );
+  }
+  @override
+  Map<String, dynamic> toJson({ValueSerializer? serializer}) {
+    serializer ??= driftRuntimeOptions.defaultSerializer;
+    return <String, dynamic>{
+      'id': serializer.toJson<int>(id),
+      'alias': serializer.toJson<String>(alias),
+      'content': serializer.toJson<String>(content),
+      'accountId': serializer.toJson<int>(accountId),
+      'createdAt': serializer.toJson<DateTime>(createdAt),
+      'cacheExpiredAt': serializer.toJson<DateTime>(cacheExpiredAt),
+    };
+  }
+
+  SnLocalRealmData copyWith(
+          {int? id,
+          String? alias,
+          String? content,
+          int? accountId,
+          DateTime? createdAt,
+          DateTime? cacheExpiredAt}) =>
+      SnLocalRealmData(
+        id: id ?? this.id,
+        alias: alias ?? this.alias,
+        content: content ?? this.content,
+        accountId: accountId ?? this.accountId,
+        createdAt: createdAt ?? this.createdAt,
+        cacheExpiredAt: cacheExpiredAt ?? this.cacheExpiredAt,
+      );
+  SnLocalRealmData copyWithCompanion(SnLocalRealmCompanion data) {
+    return SnLocalRealmData(
+      id: data.id.present ? data.id.value : this.id,
+      alias: data.alias.present ? data.alias.value : this.alias,
+      content: data.content.present ? data.content.value : this.content,
+      accountId: data.accountId.present ? data.accountId.value : this.accountId,
+      createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
+      cacheExpiredAt: data.cacheExpiredAt.present
+          ? data.cacheExpiredAt.value
+          : this.cacheExpiredAt,
+    );
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalRealmData(')
+          ..write('id: $id, ')
+          ..write('alias: $alias, ')
+          ..write('content: $content, ')
+          ..write('accountId: $accountId, ')
+          ..write('createdAt: $createdAt, ')
+          ..write('cacheExpiredAt: $cacheExpiredAt')
+          ..write(')'))
+        .toString();
+  }
+
+  @override
+  int get hashCode =>
+      Object.hash(id, alias, content, accountId, createdAt, cacheExpiredAt);
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      (other is SnLocalRealmData &&
+          other.id == this.id &&
+          other.alias == this.alias &&
+          other.content == this.content &&
+          other.accountId == this.accountId &&
+          other.createdAt == this.createdAt &&
+          other.cacheExpiredAt == this.cacheExpiredAt);
+}
+
+class SnLocalRealmCompanion extends UpdateCompanion<SnLocalRealmData> {
+  final Value<int> id;
+  final Value<String> alias;
+  final Value<String> content;
+  final Value<int> accountId;
+  final Value<DateTime> createdAt;
+  final Value<DateTime> cacheExpiredAt;
+  const SnLocalRealmCompanion({
+    this.id = const Value.absent(),
+    this.alias = const Value.absent(),
+    this.content = const Value.absent(),
+    this.accountId = const Value.absent(),
+    this.createdAt = const Value.absent(),
+    this.cacheExpiredAt = const Value.absent(),
+  });
+  SnLocalRealmCompanion.insert({
+    this.id = const Value.absent(),
+    required String alias,
+    required String content,
+    required int accountId,
+    this.createdAt = const Value.absent(),
+    required DateTime cacheExpiredAt,
+  })  : alias = Value(alias),
+        content = Value(content),
+        accountId = Value(accountId),
+        cacheExpiredAt = Value(cacheExpiredAt);
+  static Insertable<SnLocalRealmData> custom({
+    Expression<int>? id,
+    Expression<String>? alias,
+    Expression<String>? content,
+    Expression<int>? accountId,
+    Expression<DateTime>? createdAt,
+    Expression<DateTime>? cacheExpiredAt,
+  }) {
+    return RawValuesInsertable({
+      if (id != null) 'id': id,
+      if (alias != null) 'alias': alias,
+      if (content != null) 'content': content,
+      if (accountId != null) 'account_id': accountId,
+      if (createdAt != null) 'created_at': createdAt,
+      if (cacheExpiredAt != null) 'cache_expired_at': cacheExpiredAt,
+    });
+  }
+
+  SnLocalRealmCompanion copyWith(
+      {Value<int>? id,
+      Value<String>? alias,
+      Value<String>? content,
+      Value<int>? accountId,
+      Value<DateTime>? createdAt,
+      Value<DateTime>? cacheExpiredAt}) {
+    return SnLocalRealmCompanion(
+      id: id ?? this.id,
+      alias: alias ?? this.alias,
+      content: content ?? this.content,
+      accountId: accountId ?? this.accountId,
+      createdAt: createdAt ?? this.createdAt,
+      cacheExpiredAt: cacheExpiredAt ?? this.cacheExpiredAt,
+    );
+  }
+
+  @override
+  Map<String, Expression> toColumns(bool nullToAbsent) {
+    final map = <String, Expression>{};
+    if (id.present) {
+      map['id'] = Variable<int>(id.value);
+    }
+    if (alias.present) {
+      map['alias'] = Variable<String>(alias.value);
+    }
+    if (content.present) {
+      map['content'] = Variable<String>(content.value);
+    }
+    if (accountId.present) {
+      map['account_id'] = Variable<int>(accountId.value);
+    }
+    if (createdAt.present) {
+      map['created_at'] = Variable<DateTime>(createdAt.value);
+    }
+    if (cacheExpiredAt.present) {
+      map['cache_expired_at'] = Variable<DateTime>(cacheExpiredAt.value);
+    }
+    return map;
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('SnLocalRealmCompanion(')
+          ..write('id: $id, ')
+          ..write('alias: $alias, ')
+          ..write('content: $content, ')
+          ..write('accountId: $accountId, ')
+          ..write('createdAt: $createdAt, ')
+          ..write('cacheExpiredAt: $cacheExpiredAt')
+          ..write(')'))
+        .toString();
+  }
+}
+
+class DatabaseAtV4 extends GeneratedDatabase {
+  DatabaseAtV4(QueryExecutor e) : super(e);
+  late final SnLocalChatChannel snLocalChatChannel = SnLocalChatChannel(this);
+  late final SnLocalChatMessage snLocalChatMessage = SnLocalChatMessage(this);
+  late final SnLocalChannelMember snLocalChannelMember =
+      SnLocalChannelMember(this);
+  late final SnLocalKeyPair snLocalKeyPair = SnLocalKeyPair(this);
+  late final SnLocalAccount snLocalAccount = SnLocalAccount(this);
+  late final SnLocalAttachment snLocalAttachment = SnLocalAttachment(this);
+  late final SnLocalSticker snLocalSticker = SnLocalSticker(this);
+  late final SnLocalStickerPack snLocalStickerPack = SnLocalStickerPack(this);
+  late final SnLocalRealm snLocalRealm = SnLocalRealm(this);
+  late final Index idxChannelAlias = Index('idx_channel_alias',
+      'CREATE INDEX idx_channel_alias ON sn_local_chat_channel (alias)');
+  late final Index idxChatChannel = Index('idx_chat_channel',
+      'CREATE INDEX idx_chat_channel ON sn_local_chat_message (channel_id)');
+  late final Index idxAccountName = Index('idx_account_name',
+      'CREATE INDEX idx_account_name ON sn_local_account (name)');
+  late final Index idxAttachmentRid = Index('idx_attachment_rid',
+      'CREATE INDEX idx_attachment_rid ON sn_local_attachment (rid)');
+  late final Index idxAttachmentAccount = Index('idx_attachment_account',
+      'CREATE INDEX idx_attachment_account ON sn_local_attachment (account_id)');
+  late final Index idxRealmAlias = Index('idx_realm_alias',
+      'CREATE INDEX idx_realm_alias ON sn_local_realm (alias)');
+  late final Index idxRealmAccount = Index('idx_realm_account',
+      'CREATE INDEX idx_realm_account ON sn_local_realm (account_id)');
+  @override
+  Iterable<TableInfo<Table, Object?>> get allTables =>
+      allSchemaEntities.whereType<TableInfo<Table, Object?>>();
+  @override
+  List<DatabaseSchemaEntity> get allSchemaEntities => [
+        snLocalChatChannel,
+        snLocalChatMessage,
+        snLocalChannelMember,
+        snLocalKeyPair,
+        snLocalAccount,
+        snLocalAttachment,
+        snLocalSticker,
+        snLocalStickerPack,
+        snLocalRealm,
+        idxChannelAlias,
+        idxChatChannel,
+        idxAccountName,
+        idxAttachmentRid,
+        idxAttachmentAccount,
+        idxRealmAlias,
+        idxRealmAccount
+      ];
+  @override
+  int get schemaVersion => 4;
+}