Compare commits
21 Commits
3.2.0+131
...
c4d5ba5c9d
| Author | SHA1 | Date | |
|---|---|---|---|
|
c4d5ba5c9d
|
|||
|
1069669049
|
|||
|
aa648fec62
|
|||
|
541900673a
|
|||
|
265502ffd0
|
|||
|
3bd79350d1
|
|||
|
5294d1fb23
|
|||
|
ec1269dcf1
|
|||
|
edb0a25f34
|
|||
|
7cd10118cc
|
|||
|
fcddc8f345
|
|||
|
1cc34240da
|
|||
|
013f7f02bc
|
|||
|
4e79e4100f
|
|||
|
feda1f067f
|
|||
|
fe0e192a43
|
|||
|
93df294142
|
|||
|
78d65c39f3
|
|||
|
18b0dbd797
|
|||
|
80cc8cbb40
|
|||
|
646e95a9fc
|
@@ -24,6 +24,8 @@ android {
|
|||||||
ndkVersion = "29.0.13113456"
|
ndkVersion = "29.0.13113456"
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
isCoreLibraryDesugaringEnabled = true
|
||||||
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
@@ -63,6 +65,8 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
|
||||||
|
|
||||||
implementation("com.google.android.material:material:1.12.0")
|
implementation("com.google.android.material:material:1.12.0")
|
||||||
implementation("com.github.bumptech.glide:glide:4.16.0")
|
implementation("com.github.bumptech.glide:glide:4.16.0")
|
||||||
implementation("com.squareup.okhttp3:okhttp:5.1.0")
|
implementation("com.squareup.okhttp3:okhttp:5.1.0")
|
||||||
|
|||||||
@@ -133,6 +133,25 @@
|
|||||||
"other": "{} replies"
|
"other": "{} replies"
|
||||||
},
|
},
|
||||||
"forward": "Forward",
|
"forward": "Forward",
|
||||||
|
"award": "Award",
|
||||||
|
"awardPost": "Award Post",
|
||||||
|
"awardMessage": "Message",
|
||||||
|
"awardMessageHint": "Enter your award message...",
|
||||||
|
"awardAttitude": "Attitude",
|
||||||
|
"awardAttitudePositive": "Positive",
|
||||||
|
"awardAttitudeNegative": "Negative",
|
||||||
|
"awardAmount": "Amount",
|
||||||
|
"awardAmountHint": "Enter amount...",
|
||||||
|
"awardAmountRequired": "Amount is required",
|
||||||
|
"awardAmountInvalid": "Please enter a valid amount",
|
||||||
|
"awardMessageTooLong": "Message is too long (max 4096 characters)",
|
||||||
|
"awardSuccess": "Award sent successfully!",
|
||||||
|
"awardSubmit": "Award",
|
||||||
|
"awardPostPreview": "Post Preview",
|
||||||
|
"awardNoContent": "No content available",
|
||||||
|
"awardByPublisher": "By {}",
|
||||||
|
"awardBenefits": "Award Benefits",
|
||||||
|
"awardBenefitsDescription": "Awarding this post increases its value and visibility. Higher valued posts have a better chance of being featured and highlighted in the community.",
|
||||||
"repliedTo": "Replied to",
|
"repliedTo": "Replied to",
|
||||||
"forwarded": "Forwarded",
|
"forwarded": "Forwarded",
|
||||||
"hasAttachments": {
|
"hasAttachments": {
|
||||||
@@ -452,6 +471,8 @@
|
|||||||
"close": "Close",
|
"close": "Close",
|
||||||
"drafts": "Drafts",
|
"drafts": "Drafts",
|
||||||
"noDrafts": "No drafts yet",
|
"noDrafts": "No drafts yet",
|
||||||
|
"searchDrafts": "Search drafts...",
|
||||||
|
"noSearchResults": "No search results",
|
||||||
"articleDrafts": "Article drafts",
|
"articleDrafts": "Article drafts",
|
||||||
"postDrafts": "Post drafts",
|
"postDrafts": "Post drafts",
|
||||||
"saveDraft": "Save draft",
|
"saveDraft": "Save draft",
|
||||||
@@ -498,6 +519,10 @@
|
|||||||
"contactMethodSetPrimary": "Set as Primary",
|
"contactMethodSetPrimary": "Set as Primary",
|
||||||
"contactMethodSetPrimaryHint": "Set this contact method as your primary contact method for account recovery and notifications",
|
"contactMethodSetPrimaryHint": "Set this contact method as your primary contact method for account recovery and notifications",
|
||||||
"contactMethodDeleteHint": "Are you sure to delete this contact method? This action cannot be undone.",
|
"contactMethodDeleteHint": "Are you sure to delete this contact method? This action cannot be undone.",
|
||||||
|
"contactMethodMakePublic": "Make Public",
|
||||||
|
"contactMethodMakePrivate": "Make Private",
|
||||||
|
"contactMethodPublic": "Public",
|
||||||
|
"contactMethodPrivate": "Private",
|
||||||
"chatNotifyLevel": "Notify Level",
|
"chatNotifyLevel": "Notify Level",
|
||||||
"chatNotifyLevelDescription": "Decide how many notifications you will receive.",
|
"chatNotifyLevelDescription": "Decide how many notifications you will receive.",
|
||||||
"chatNotifyLevelAll": "All",
|
"chatNotifyLevelAll": "All",
|
||||||
@@ -977,5 +1002,19 @@
|
|||||||
"pinned": "Pinned",
|
"pinned": "Pinned",
|
||||||
"noResultsFound": "No results found",
|
"noResultsFound": "No results found",
|
||||||
"toggleFilters": "Toggle filters",
|
"toggleFilters": "Toggle filters",
|
||||||
"notableDayNext": "{} is in"
|
"notableDayNext": "{} is in",
|
||||||
|
"expandPoll": "Expand Poll",
|
||||||
|
"collapsePoll": "Collapse Poll",
|
||||||
|
"embedView": "Embed View",
|
||||||
|
"embedUri": "Embed URI",
|
||||||
|
"aspectRatio": "Aspect Ratio",
|
||||||
|
"renderer": "Renderer",
|
||||||
|
"addEmbed": "Add Embed",
|
||||||
|
"editEmbed": "Edit Embed",
|
||||||
|
"deleteEmbed": "Delete Embed",
|
||||||
|
"deleteEmbedConfirm": "Are you sure you want to delete this embed?",
|
||||||
|
"currentEmbed": "Current Embed",
|
||||||
|
"noEmbed": "No embed yet",
|
||||||
|
"save": "Save",
|
||||||
|
"webView": "Web View"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,15 @@ import 'package:drift/drift.dart';
|
|||||||
|
|
||||||
class PostDrafts extends Table {
|
class PostDrafts extends Table {
|
||||||
TextColumn get id => text()();
|
TextColumn get id => text()();
|
||||||
TextColumn get post => text()(); // Store SnPost model as JSON string
|
// Searchable fields stored separately for performance
|
||||||
|
TextColumn get title => text().nullable()();
|
||||||
|
TextColumn get description => text().nullable()();
|
||||||
|
TextColumn get content => text().nullable()();
|
||||||
|
IntColumn get visibility => integer().withDefault(const Constant(0))();
|
||||||
|
IntColumn get type => integer().withDefault(const Constant(0))();
|
||||||
DateTimeColumn get lastModified => dateTime()();
|
DateTimeColumn get lastModified => dateTime()();
|
||||||
|
// Full post data stored as JSON for complete restoration
|
||||||
|
TextColumn get postData => text()();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Set<Column> get primaryKey => {id};
|
Set<Column> get primaryKey => {id};
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class AppDatabase extends _$AppDatabase {
|
|||||||
AppDatabase(super.e);
|
AppDatabase(super.e);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 4;
|
int get schemaVersion => 6;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration => MigrationStrategy(
|
MigrationStrategy get migration => MigrationStrategy(
|
||||||
@@ -28,9 +28,67 @@ class AppDatabase extends _$AppDatabase {
|
|||||||
// Drop old draft tables if they exist
|
// Drop old draft tables if they exist
|
||||||
await m.createTable(postDrafts);
|
await m.createTable(postDrafts);
|
||||||
}
|
}
|
||||||
|
if (from < 6) {
|
||||||
|
// Migrate from old schema to new schema with separate searchable fields
|
||||||
|
await _migrateToVersion6(m);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Future<void> _migrateToVersion6(Migrator m) async {
|
||||||
|
// Rename existing table to old if it exists
|
||||||
|
try {
|
||||||
|
await customStatement(
|
||||||
|
'ALTER TABLE post_drafts RENAME TO post_drafts_old',
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
// Table might not exist
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop the table
|
||||||
|
await customStatement('DROP TABLE IF EXISTS post_drafts');
|
||||||
|
|
||||||
|
// Create new table
|
||||||
|
await m.createTable(postDrafts);
|
||||||
|
|
||||||
|
// Migrate existing data if any
|
||||||
|
try {
|
||||||
|
final oldDrafts =
|
||||||
|
await customSelect(
|
||||||
|
'SELECT id, post, lastModified FROM post_drafts_old',
|
||||||
|
readsFrom: {postDrafts},
|
||||||
|
).get();
|
||||||
|
|
||||||
|
for (final row in oldDrafts) {
|
||||||
|
final postJson = row.read<String>('post');
|
||||||
|
final id = row.read<String>('id');
|
||||||
|
final lastModified = row.read<DateTime>('lastModified');
|
||||||
|
|
||||||
|
if (postJson.isNotEmpty) {
|
||||||
|
final post = SnPost.fromJson(jsonDecode(postJson));
|
||||||
|
await into(postDrafts).insert(
|
||||||
|
PostDraftsCompanion(
|
||||||
|
id: Value(id),
|
||||||
|
title: Value(post.title),
|
||||||
|
description: Value(post.description),
|
||||||
|
content: Value(post.content),
|
||||||
|
visibility: Value(post.visibility),
|
||||||
|
type: Value(post.type),
|
||||||
|
lastModified: Value(lastModified),
|
||||||
|
postData: Value(postJson),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop old table
|
||||||
|
await customStatement('DROP TABLE IF EXISTS post_drafts_old');
|
||||||
|
} catch (e) {
|
||||||
|
// If migration fails, just recreate the table
|
||||||
|
await m.createTable(postDrafts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Methods for chat messages
|
// Methods for chat messages
|
||||||
Future<List<ChatMessage>> getMessagesForRoom(
|
Future<List<ChatMessage>> getMessagesForRoom(
|
||||||
String roomId, {
|
String roomId, {
|
||||||
@@ -69,7 +127,9 @@ class AppDatabase extends _$AppDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<int> getTotalMessagesForRoom(String roomId) {
|
Future<int> getTotalMessagesForRoom(String roomId) {
|
||||||
return (select(chatMessages)..where((m) => m.roomId.equals(roomId))).get().then((list) => list.length);
|
return (select(
|
||||||
|
chatMessages,
|
||||||
|
)..where((m) => m.roomId.equals(roomId))).get().then((list) => list.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<LocalChatMessage>> searchMessages(
|
Future<List<LocalChatMessage>> searchMessages(
|
||||||
@@ -85,10 +145,6 @@ class AppDatabase extends _$AppDatabase {
|
|||||||
..where((m) => m.content.like('%${query.toLowerCase()}%'));
|
..where((m) => m.content.like('%${query.toLowerCase()}%'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
final messages =
|
final messages =
|
||||||
await (selectStatement
|
await (selectStatement
|
||||||
..orderBy([(m) => OrderingTerm.desc(m.createdAt)]))
|
..orderBy([(m) => OrderingTerm.desc(m.createdAt)]))
|
||||||
@@ -129,10 +185,31 @@ class AppDatabase extends _$AppDatabase {
|
|||||||
Future<List<SnPost>> getAllPostDrafts() async {
|
Future<List<SnPost>> getAllPostDrafts() async {
|
||||||
final drafts = await select(postDrafts).get();
|
final drafts = await select(postDrafts).get();
|
||||||
return drafts
|
return drafts
|
||||||
.map((draft) => SnPost.fromJson(jsonDecode(draft.post)))
|
.map((draft) => SnPost.fromJson(jsonDecode(draft.postData)))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<PostDraft>> getAllPostDraftRecords() async {
|
||||||
|
return await select(postDrafts).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<PostDraft>> searchPostDrafts(String query) async {
|
||||||
|
if (query.isEmpty) {
|
||||||
|
return await select(postDrafts).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
final searchTerm = '%${query.toLowerCase()}%';
|
||||||
|
return await (select(postDrafts)
|
||||||
|
..where(
|
||||||
|
(draft) =>
|
||||||
|
draft.title.like(searchTerm) |
|
||||||
|
draft.description.like(searchTerm) |
|
||||||
|
draft.content.like(searchTerm),
|
||||||
|
)
|
||||||
|
..orderBy([(draft) => OrderingTerm.desc(draft.lastModified)]))
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> addPostDraft(PostDraftsCompanion entry) async {
|
Future<void> addPostDraft(PostDraftsCompanion entry) async {
|
||||||
await into(postDrafts).insert(entry, mode: InsertMode.replace);
|
await into(postDrafts).insert(entry, mode: InsertMode.replace);
|
||||||
}
|
}
|
||||||
@@ -144,4 +221,9 @@ class AppDatabase extends _$AppDatabase {
|
|||||||
Future<void> clearAllPostDrafts() async {
|
Future<void> clearAllPostDrafts() async {
|
||||||
await delete(postDrafts).go();
|
await delete(postDrafts).go();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<PostDraft?> getPostDraftById(String id) async {
|
||||||
|
return await (select(postDrafts)
|
||||||
|
..where((tbl) => tbl.id.equals(id))).getSingleOrNull();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -584,14 +584,58 @@ class $PostDraftsTable extends PostDrafts
|
|||||||
type: DriftSqlType.string,
|
type: DriftSqlType.string,
|
||||||
requiredDuringInsert: true,
|
requiredDuringInsert: true,
|
||||||
);
|
);
|
||||||
static const VerificationMeta _postMeta = const VerificationMeta('post');
|
static const VerificationMeta _titleMeta = const VerificationMeta('title');
|
||||||
@override
|
@override
|
||||||
late final GeneratedColumn<String> post = GeneratedColumn<String>(
|
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
||||||
'post',
|
'title',
|
||||||
|
aliasedName,
|
||||||
|
true,
|
||||||
|
type: DriftSqlType.string,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
);
|
||||||
|
static const VerificationMeta _descriptionMeta = const VerificationMeta(
|
||||||
|
'description',
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
late final GeneratedColumn<String> description = GeneratedColumn<String>(
|
||||||
|
'description',
|
||||||
|
aliasedName,
|
||||||
|
true,
|
||||||
|
type: DriftSqlType.string,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
);
|
||||||
|
static const VerificationMeta _contentMeta = const VerificationMeta(
|
||||||
|
'content',
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
late final GeneratedColumn<String> content = GeneratedColumn<String>(
|
||||||
|
'content',
|
||||||
|
aliasedName,
|
||||||
|
true,
|
||||||
|
type: DriftSqlType.string,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
);
|
||||||
|
static const VerificationMeta _visibilityMeta = const VerificationMeta(
|
||||||
|
'visibility',
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
late final GeneratedColumn<int> visibility = GeneratedColumn<int>(
|
||||||
|
'visibility',
|
||||||
aliasedName,
|
aliasedName,
|
||||||
false,
|
false,
|
||||||
type: DriftSqlType.string,
|
type: DriftSqlType.int,
|
||||||
requiredDuringInsert: true,
|
requiredDuringInsert: false,
|
||||||
|
defaultValue: const Constant(0),
|
||||||
|
);
|
||||||
|
static const VerificationMeta _typeMeta = const VerificationMeta('type');
|
||||||
|
@override
|
||||||
|
late final GeneratedColumn<int> type = GeneratedColumn<int>(
|
||||||
|
'type',
|
||||||
|
aliasedName,
|
||||||
|
false,
|
||||||
|
type: DriftSqlType.int,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
defaultValue: const Constant(0),
|
||||||
);
|
);
|
||||||
static const VerificationMeta _lastModifiedMeta = const VerificationMeta(
|
static const VerificationMeta _lastModifiedMeta = const VerificationMeta(
|
||||||
'lastModified',
|
'lastModified',
|
||||||
@@ -604,8 +648,28 @@ class $PostDraftsTable extends PostDrafts
|
|||||||
type: DriftSqlType.dateTime,
|
type: DriftSqlType.dateTime,
|
||||||
requiredDuringInsert: true,
|
requiredDuringInsert: true,
|
||||||
);
|
);
|
||||||
|
static const VerificationMeta _postDataMeta = const VerificationMeta(
|
||||||
|
'postData',
|
||||||
|
);
|
||||||
@override
|
@override
|
||||||
List<GeneratedColumn> get $columns => [id, post, lastModified];
|
late final GeneratedColumn<String> postData = GeneratedColumn<String>(
|
||||||
|
'post_data',
|
||||||
|
aliasedName,
|
||||||
|
false,
|
||||||
|
type: DriftSqlType.string,
|
||||||
|
requiredDuringInsert: true,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
List<GeneratedColumn> get $columns => [
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
content,
|
||||||
|
visibility,
|
||||||
|
type,
|
||||||
|
lastModified,
|
||||||
|
postData,
|
||||||
|
];
|
||||||
@override
|
@override
|
||||||
String get aliasedName => _alias ?? actualTableName;
|
String get aliasedName => _alias ?? actualTableName;
|
||||||
@override
|
@override
|
||||||
@@ -623,13 +687,38 @@ class $PostDraftsTable extends PostDrafts
|
|||||||
} else if (isInserting) {
|
} else if (isInserting) {
|
||||||
context.missing(_idMeta);
|
context.missing(_idMeta);
|
||||||
}
|
}
|
||||||
if (data.containsKey('post')) {
|
if (data.containsKey('title')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
_postMeta,
|
_titleMeta,
|
||||||
post.isAcceptableOrUnknown(data['post']!, _postMeta),
|
title.isAcceptableOrUnknown(data['title']!, _titleMeta),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (data.containsKey('description')) {
|
||||||
|
context.handle(
|
||||||
|
_descriptionMeta,
|
||||||
|
description.isAcceptableOrUnknown(
|
||||||
|
data['description']!,
|
||||||
|
_descriptionMeta,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (data.containsKey('content')) {
|
||||||
|
context.handle(
|
||||||
|
_contentMeta,
|
||||||
|
content.isAcceptableOrUnknown(data['content']!, _contentMeta),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (data.containsKey('visibility')) {
|
||||||
|
context.handle(
|
||||||
|
_visibilityMeta,
|
||||||
|
visibility.isAcceptableOrUnknown(data['visibility']!, _visibilityMeta),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (data.containsKey('type')) {
|
||||||
|
context.handle(
|
||||||
|
_typeMeta,
|
||||||
|
type.isAcceptableOrUnknown(data['type']!, _typeMeta),
|
||||||
);
|
);
|
||||||
} else if (isInserting) {
|
|
||||||
context.missing(_postMeta);
|
|
||||||
}
|
}
|
||||||
if (data.containsKey('last_modified')) {
|
if (data.containsKey('last_modified')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
@@ -642,6 +731,14 @@ class $PostDraftsTable extends PostDrafts
|
|||||||
} else if (isInserting) {
|
} else if (isInserting) {
|
||||||
context.missing(_lastModifiedMeta);
|
context.missing(_lastModifiedMeta);
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('post_data')) {
|
||||||
|
context.handle(
|
||||||
|
_postDataMeta,
|
||||||
|
postData.isAcceptableOrUnknown(data['post_data']!, _postDataMeta),
|
||||||
|
);
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_postDataMeta);
|
||||||
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,16 +753,38 @@ class $PostDraftsTable extends PostDrafts
|
|||||||
DriftSqlType.string,
|
DriftSqlType.string,
|
||||||
data['${effectivePrefix}id'],
|
data['${effectivePrefix}id'],
|
||||||
)!,
|
)!,
|
||||||
post:
|
title: attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.string,
|
||||||
|
data['${effectivePrefix}title'],
|
||||||
|
),
|
||||||
|
description: attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.string,
|
||||||
|
data['${effectivePrefix}description'],
|
||||||
|
),
|
||||||
|
content: attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.string,
|
||||||
|
data['${effectivePrefix}content'],
|
||||||
|
),
|
||||||
|
visibility:
|
||||||
attachedDatabase.typeMapping.read(
|
attachedDatabase.typeMapping.read(
|
||||||
DriftSqlType.string,
|
DriftSqlType.int,
|
||||||
data['${effectivePrefix}post'],
|
data['${effectivePrefix}visibility'],
|
||||||
|
)!,
|
||||||
|
type:
|
||||||
|
attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.int,
|
||||||
|
data['${effectivePrefix}type'],
|
||||||
)!,
|
)!,
|
||||||
lastModified:
|
lastModified:
|
||||||
attachedDatabase.typeMapping.read(
|
attachedDatabase.typeMapping.read(
|
||||||
DriftSqlType.dateTime,
|
DriftSqlType.dateTime,
|
||||||
data['${effectivePrefix}last_modified'],
|
data['${effectivePrefix}last_modified'],
|
||||||
)!,
|
)!,
|
||||||
|
postData:
|
||||||
|
attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.string,
|
||||||
|
data['${effectivePrefix}post_data'],
|
||||||
|
)!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -677,27 +796,60 @@ class $PostDraftsTable extends PostDrafts
|
|||||||
|
|
||||||
class PostDraft extends DataClass implements Insertable<PostDraft> {
|
class PostDraft extends DataClass implements Insertable<PostDraft> {
|
||||||
final String id;
|
final String id;
|
||||||
final String post;
|
final String? title;
|
||||||
|
final String? description;
|
||||||
|
final String? content;
|
||||||
|
final int visibility;
|
||||||
|
final int type;
|
||||||
final DateTime lastModified;
|
final DateTime lastModified;
|
||||||
|
final String postData;
|
||||||
const PostDraft({
|
const PostDraft({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.post,
|
this.title,
|
||||||
|
this.description,
|
||||||
|
this.content,
|
||||||
|
required this.visibility,
|
||||||
|
required this.type,
|
||||||
required this.lastModified,
|
required this.lastModified,
|
||||||
|
required this.postData,
|
||||||
});
|
});
|
||||||
@override
|
@override
|
||||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
final map = <String, Expression>{};
|
final map = <String, Expression>{};
|
||||||
map['id'] = Variable<String>(id);
|
map['id'] = Variable<String>(id);
|
||||||
map['post'] = Variable<String>(post);
|
if (!nullToAbsent || title != null) {
|
||||||
|
map['title'] = Variable<String>(title);
|
||||||
|
}
|
||||||
|
if (!nullToAbsent || description != null) {
|
||||||
|
map['description'] = Variable<String>(description);
|
||||||
|
}
|
||||||
|
if (!nullToAbsent || content != null) {
|
||||||
|
map['content'] = Variable<String>(content);
|
||||||
|
}
|
||||||
|
map['visibility'] = Variable<int>(visibility);
|
||||||
|
map['type'] = Variable<int>(type);
|
||||||
map['last_modified'] = Variable<DateTime>(lastModified);
|
map['last_modified'] = Variable<DateTime>(lastModified);
|
||||||
|
map['post_data'] = Variable<String>(postData);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
PostDraftsCompanion toCompanion(bool nullToAbsent) {
|
PostDraftsCompanion toCompanion(bool nullToAbsent) {
|
||||||
return PostDraftsCompanion(
|
return PostDraftsCompanion(
|
||||||
id: Value(id),
|
id: Value(id),
|
||||||
post: Value(post),
|
title:
|
||||||
|
title == null && nullToAbsent ? const Value.absent() : Value(title),
|
||||||
|
description:
|
||||||
|
description == null && nullToAbsent
|
||||||
|
? const Value.absent()
|
||||||
|
: Value(description),
|
||||||
|
content:
|
||||||
|
content == null && nullToAbsent
|
||||||
|
? const Value.absent()
|
||||||
|
: Value(content),
|
||||||
|
visibility: Value(visibility),
|
||||||
|
type: Value(type),
|
||||||
lastModified: Value(lastModified),
|
lastModified: Value(lastModified),
|
||||||
|
postData: Value(postData),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -708,8 +860,13 @@ class PostDraft extends DataClass implements Insertable<PostDraft> {
|
|||||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
return PostDraft(
|
return PostDraft(
|
||||||
id: serializer.fromJson<String>(json['id']),
|
id: serializer.fromJson<String>(json['id']),
|
||||||
post: serializer.fromJson<String>(json['post']),
|
title: serializer.fromJson<String?>(json['title']),
|
||||||
|
description: serializer.fromJson<String?>(json['description']),
|
||||||
|
content: serializer.fromJson<String?>(json['content']),
|
||||||
|
visibility: serializer.fromJson<int>(json['visibility']),
|
||||||
|
type: serializer.fromJson<int>(json['type']),
|
||||||
lastModified: serializer.fromJson<DateTime>(json['lastModified']),
|
lastModified: serializer.fromJson<DateTime>(json['lastModified']),
|
||||||
|
postData: serializer.fromJson<String>(json['postData']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@override
|
@override
|
||||||
@@ -717,25 +874,50 @@ class PostDraft extends DataClass implements Insertable<PostDraft> {
|
|||||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
return <String, dynamic>{
|
return <String, dynamic>{
|
||||||
'id': serializer.toJson<String>(id),
|
'id': serializer.toJson<String>(id),
|
||||||
'post': serializer.toJson<String>(post),
|
'title': serializer.toJson<String?>(title),
|
||||||
|
'description': serializer.toJson<String?>(description),
|
||||||
|
'content': serializer.toJson<String?>(content),
|
||||||
|
'visibility': serializer.toJson<int>(visibility),
|
||||||
|
'type': serializer.toJson<int>(type),
|
||||||
'lastModified': serializer.toJson<DateTime>(lastModified),
|
'lastModified': serializer.toJson<DateTime>(lastModified),
|
||||||
|
'postData': serializer.toJson<String>(postData),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
PostDraft copyWith({String? id, String? post, DateTime? lastModified}) =>
|
PostDraft copyWith({
|
||||||
PostDraft(
|
String? id,
|
||||||
id: id ?? this.id,
|
Value<String?> title = const Value.absent(),
|
||||||
post: post ?? this.post,
|
Value<String?> description = const Value.absent(),
|
||||||
lastModified: lastModified ?? this.lastModified,
|
Value<String?> content = const Value.absent(),
|
||||||
);
|
int? visibility,
|
||||||
|
int? type,
|
||||||
|
DateTime? lastModified,
|
||||||
|
String? postData,
|
||||||
|
}) => PostDraft(
|
||||||
|
id: id ?? this.id,
|
||||||
|
title: title.present ? title.value : this.title,
|
||||||
|
description: description.present ? description.value : this.description,
|
||||||
|
content: content.present ? content.value : this.content,
|
||||||
|
visibility: visibility ?? this.visibility,
|
||||||
|
type: type ?? this.type,
|
||||||
|
lastModified: lastModified ?? this.lastModified,
|
||||||
|
postData: postData ?? this.postData,
|
||||||
|
);
|
||||||
PostDraft copyWithCompanion(PostDraftsCompanion data) {
|
PostDraft copyWithCompanion(PostDraftsCompanion data) {
|
||||||
return PostDraft(
|
return PostDraft(
|
||||||
id: data.id.present ? data.id.value : this.id,
|
id: data.id.present ? data.id.value : this.id,
|
||||||
post: data.post.present ? data.post.value : this.post,
|
title: data.title.present ? data.title.value : this.title,
|
||||||
|
description:
|
||||||
|
data.description.present ? data.description.value : this.description,
|
||||||
|
content: data.content.present ? data.content.value : this.content,
|
||||||
|
visibility:
|
||||||
|
data.visibility.present ? data.visibility.value : this.visibility,
|
||||||
|
type: data.type.present ? data.type.value : this.type,
|
||||||
lastModified:
|
lastModified:
|
||||||
data.lastModified.present
|
data.lastModified.present
|
||||||
? data.lastModified.value
|
? data.lastModified.value
|
||||||
: this.lastModified,
|
: this.lastModified,
|
||||||
|
postData: data.postData.present ? data.postData.value : this.postData,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -743,66 +925,120 @@ class PostDraft extends DataClass implements Insertable<PostDraft> {
|
|||||||
String toString() {
|
String toString() {
|
||||||
return (StringBuffer('PostDraft(')
|
return (StringBuffer('PostDraft(')
|
||||||
..write('id: $id, ')
|
..write('id: $id, ')
|
||||||
..write('post: $post, ')
|
..write('title: $title, ')
|
||||||
..write('lastModified: $lastModified')
|
..write('description: $description, ')
|
||||||
|
..write('content: $content, ')
|
||||||
|
..write('visibility: $visibility, ')
|
||||||
|
..write('type: $type, ')
|
||||||
|
..write('lastModified: $lastModified, ')
|
||||||
|
..write('postData: $postData')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(id, post, lastModified);
|
int get hashCode => Object.hash(
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
content,
|
||||||
|
visibility,
|
||||||
|
type,
|
||||||
|
lastModified,
|
||||||
|
postData,
|
||||||
|
);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
(other is PostDraft &&
|
(other is PostDraft &&
|
||||||
other.id == this.id &&
|
other.id == this.id &&
|
||||||
other.post == this.post &&
|
other.title == this.title &&
|
||||||
other.lastModified == this.lastModified);
|
other.description == this.description &&
|
||||||
|
other.content == this.content &&
|
||||||
|
other.visibility == this.visibility &&
|
||||||
|
other.type == this.type &&
|
||||||
|
other.lastModified == this.lastModified &&
|
||||||
|
other.postData == this.postData);
|
||||||
}
|
}
|
||||||
|
|
||||||
class PostDraftsCompanion extends UpdateCompanion<PostDraft> {
|
class PostDraftsCompanion extends UpdateCompanion<PostDraft> {
|
||||||
final Value<String> id;
|
final Value<String> id;
|
||||||
final Value<String> post;
|
final Value<String?> title;
|
||||||
|
final Value<String?> description;
|
||||||
|
final Value<String?> content;
|
||||||
|
final Value<int> visibility;
|
||||||
|
final Value<int> type;
|
||||||
final Value<DateTime> lastModified;
|
final Value<DateTime> lastModified;
|
||||||
|
final Value<String> postData;
|
||||||
final Value<int> rowid;
|
final Value<int> rowid;
|
||||||
const PostDraftsCompanion({
|
const PostDraftsCompanion({
|
||||||
this.id = const Value.absent(),
|
this.id = const Value.absent(),
|
||||||
this.post = const Value.absent(),
|
this.title = const Value.absent(),
|
||||||
|
this.description = const Value.absent(),
|
||||||
|
this.content = const Value.absent(),
|
||||||
|
this.visibility = const Value.absent(),
|
||||||
|
this.type = const Value.absent(),
|
||||||
this.lastModified = const Value.absent(),
|
this.lastModified = const Value.absent(),
|
||||||
|
this.postData = const Value.absent(),
|
||||||
this.rowid = const Value.absent(),
|
this.rowid = const Value.absent(),
|
||||||
});
|
});
|
||||||
PostDraftsCompanion.insert({
|
PostDraftsCompanion.insert({
|
||||||
required String id,
|
required String id,
|
||||||
required String post,
|
this.title = const Value.absent(),
|
||||||
|
this.description = const Value.absent(),
|
||||||
|
this.content = const Value.absent(),
|
||||||
|
this.visibility = const Value.absent(),
|
||||||
|
this.type = const Value.absent(),
|
||||||
required DateTime lastModified,
|
required DateTime lastModified,
|
||||||
|
required String postData,
|
||||||
this.rowid = const Value.absent(),
|
this.rowid = const Value.absent(),
|
||||||
}) : id = Value(id),
|
}) : id = Value(id),
|
||||||
post = Value(post),
|
lastModified = Value(lastModified),
|
||||||
lastModified = Value(lastModified);
|
postData = Value(postData);
|
||||||
static Insertable<PostDraft> custom({
|
static Insertable<PostDraft> custom({
|
||||||
Expression<String>? id,
|
Expression<String>? id,
|
||||||
Expression<String>? post,
|
Expression<String>? title,
|
||||||
|
Expression<String>? description,
|
||||||
|
Expression<String>? content,
|
||||||
|
Expression<int>? visibility,
|
||||||
|
Expression<int>? type,
|
||||||
Expression<DateTime>? lastModified,
|
Expression<DateTime>? lastModified,
|
||||||
|
Expression<String>? postData,
|
||||||
Expression<int>? rowid,
|
Expression<int>? rowid,
|
||||||
}) {
|
}) {
|
||||||
return RawValuesInsertable({
|
return RawValuesInsertable({
|
||||||
if (id != null) 'id': id,
|
if (id != null) 'id': id,
|
||||||
if (post != null) 'post': post,
|
if (title != null) 'title': title,
|
||||||
|
if (description != null) 'description': description,
|
||||||
|
if (content != null) 'content': content,
|
||||||
|
if (visibility != null) 'visibility': visibility,
|
||||||
|
if (type != null) 'type': type,
|
||||||
if (lastModified != null) 'last_modified': lastModified,
|
if (lastModified != null) 'last_modified': lastModified,
|
||||||
|
if (postData != null) 'post_data': postData,
|
||||||
if (rowid != null) 'rowid': rowid,
|
if (rowid != null) 'rowid': rowid,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
PostDraftsCompanion copyWith({
|
PostDraftsCompanion copyWith({
|
||||||
Value<String>? id,
|
Value<String>? id,
|
||||||
Value<String>? post,
|
Value<String?>? title,
|
||||||
|
Value<String?>? description,
|
||||||
|
Value<String?>? content,
|
||||||
|
Value<int>? visibility,
|
||||||
|
Value<int>? type,
|
||||||
Value<DateTime>? lastModified,
|
Value<DateTime>? lastModified,
|
||||||
|
Value<String>? postData,
|
||||||
Value<int>? rowid,
|
Value<int>? rowid,
|
||||||
}) {
|
}) {
|
||||||
return PostDraftsCompanion(
|
return PostDraftsCompanion(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
post: post ?? this.post,
|
title: title ?? this.title,
|
||||||
|
description: description ?? this.description,
|
||||||
|
content: content ?? this.content,
|
||||||
|
visibility: visibility ?? this.visibility,
|
||||||
|
type: type ?? this.type,
|
||||||
lastModified: lastModified ?? this.lastModified,
|
lastModified: lastModified ?? this.lastModified,
|
||||||
|
postData: postData ?? this.postData,
|
||||||
rowid: rowid ?? this.rowid,
|
rowid: rowid ?? this.rowid,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -813,12 +1049,27 @@ class PostDraftsCompanion extends UpdateCompanion<PostDraft> {
|
|||||||
if (id.present) {
|
if (id.present) {
|
||||||
map['id'] = Variable<String>(id.value);
|
map['id'] = Variable<String>(id.value);
|
||||||
}
|
}
|
||||||
if (post.present) {
|
if (title.present) {
|
||||||
map['post'] = Variable<String>(post.value);
|
map['title'] = Variable<String>(title.value);
|
||||||
|
}
|
||||||
|
if (description.present) {
|
||||||
|
map['description'] = Variable<String>(description.value);
|
||||||
|
}
|
||||||
|
if (content.present) {
|
||||||
|
map['content'] = Variable<String>(content.value);
|
||||||
|
}
|
||||||
|
if (visibility.present) {
|
||||||
|
map['visibility'] = Variable<int>(visibility.value);
|
||||||
|
}
|
||||||
|
if (type.present) {
|
||||||
|
map['type'] = Variable<int>(type.value);
|
||||||
}
|
}
|
||||||
if (lastModified.present) {
|
if (lastModified.present) {
|
||||||
map['last_modified'] = Variable<DateTime>(lastModified.value);
|
map['last_modified'] = Variable<DateTime>(lastModified.value);
|
||||||
}
|
}
|
||||||
|
if (postData.present) {
|
||||||
|
map['post_data'] = Variable<String>(postData.value);
|
||||||
|
}
|
||||||
if (rowid.present) {
|
if (rowid.present) {
|
||||||
map['rowid'] = Variable<int>(rowid.value);
|
map['rowid'] = Variable<int>(rowid.value);
|
||||||
}
|
}
|
||||||
@@ -829,8 +1080,13 @@ class PostDraftsCompanion extends UpdateCompanion<PostDraft> {
|
|||||||
String toString() {
|
String toString() {
|
||||||
return (StringBuffer('PostDraftsCompanion(')
|
return (StringBuffer('PostDraftsCompanion(')
|
||||||
..write('id: $id, ')
|
..write('id: $id, ')
|
||||||
..write('post: $post, ')
|
..write('title: $title, ')
|
||||||
|
..write('description: $description, ')
|
||||||
|
..write('content: $content, ')
|
||||||
|
..write('visibility: $visibility, ')
|
||||||
|
..write('type: $type, ')
|
||||||
..write('lastModified: $lastModified, ')
|
..write('lastModified: $lastModified, ')
|
||||||
|
..write('postData: $postData, ')
|
||||||
..write('rowid: $rowid')
|
..write('rowid: $rowid')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
@@ -1140,15 +1396,25 @@ typedef $$ChatMessagesTableProcessedTableManager =
|
|||||||
typedef $$PostDraftsTableCreateCompanionBuilder =
|
typedef $$PostDraftsTableCreateCompanionBuilder =
|
||||||
PostDraftsCompanion Function({
|
PostDraftsCompanion Function({
|
||||||
required String id,
|
required String id,
|
||||||
required String post,
|
Value<String?> title,
|
||||||
|
Value<String?> description,
|
||||||
|
Value<String?> content,
|
||||||
|
Value<int> visibility,
|
||||||
|
Value<int> type,
|
||||||
required DateTime lastModified,
|
required DateTime lastModified,
|
||||||
|
required String postData,
|
||||||
Value<int> rowid,
|
Value<int> rowid,
|
||||||
});
|
});
|
||||||
typedef $$PostDraftsTableUpdateCompanionBuilder =
|
typedef $$PostDraftsTableUpdateCompanionBuilder =
|
||||||
PostDraftsCompanion Function({
|
PostDraftsCompanion Function({
|
||||||
Value<String> id,
|
Value<String> id,
|
||||||
Value<String> post,
|
Value<String?> title,
|
||||||
|
Value<String?> description,
|
||||||
|
Value<String?> content,
|
||||||
|
Value<int> visibility,
|
||||||
|
Value<int> type,
|
||||||
Value<DateTime> lastModified,
|
Value<DateTime> lastModified,
|
||||||
|
Value<String> postData,
|
||||||
Value<int> rowid,
|
Value<int> rowid,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1166,8 +1432,28 @@ class $$PostDraftsTableFilterComposer
|
|||||||
builder: (column) => ColumnFilters(column),
|
builder: (column) => ColumnFilters(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
ColumnFilters<String> get post => $composableBuilder(
|
ColumnFilters<String> get title => $composableBuilder(
|
||||||
column: $table.post,
|
column: $table.title,
|
||||||
|
builder: (column) => ColumnFilters(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
ColumnFilters<String> get description => $composableBuilder(
|
||||||
|
column: $table.description,
|
||||||
|
builder: (column) => ColumnFilters(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
ColumnFilters<String> get content => $composableBuilder(
|
||||||
|
column: $table.content,
|
||||||
|
builder: (column) => ColumnFilters(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
ColumnFilters<int> get visibility => $composableBuilder(
|
||||||
|
column: $table.visibility,
|
||||||
|
builder: (column) => ColumnFilters(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
ColumnFilters<int> get type => $composableBuilder(
|
||||||
|
column: $table.type,
|
||||||
builder: (column) => ColumnFilters(column),
|
builder: (column) => ColumnFilters(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1175,6 +1461,11 @@ class $$PostDraftsTableFilterComposer
|
|||||||
column: $table.lastModified,
|
column: $table.lastModified,
|
||||||
builder: (column) => ColumnFilters(column),
|
builder: (column) => ColumnFilters(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ColumnFilters<String> get postData => $composableBuilder(
|
||||||
|
column: $table.postData,
|
||||||
|
builder: (column) => ColumnFilters(column),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class $$PostDraftsTableOrderingComposer
|
class $$PostDraftsTableOrderingComposer
|
||||||
@@ -1191,8 +1482,28 @@ class $$PostDraftsTableOrderingComposer
|
|||||||
builder: (column) => ColumnOrderings(column),
|
builder: (column) => ColumnOrderings(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
ColumnOrderings<String> get post => $composableBuilder(
|
ColumnOrderings<String> get title => $composableBuilder(
|
||||||
column: $table.post,
|
column: $table.title,
|
||||||
|
builder: (column) => ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
ColumnOrderings<String> get description => $composableBuilder(
|
||||||
|
column: $table.description,
|
||||||
|
builder: (column) => ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
ColumnOrderings<String> get content => $composableBuilder(
|
||||||
|
column: $table.content,
|
||||||
|
builder: (column) => ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
ColumnOrderings<int> get visibility => $composableBuilder(
|
||||||
|
column: $table.visibility,
|
||||||
|
builder: (column) => ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
ColumnOrderings<int> get type => $composableBuilder(
|
||||||
|
column: $table.type,
|
||||||
builder: (column) => ColumnOrderings(column),
|
builder: (column) => ColumnOrderings(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1200,6 +1511,11 @@ class $$PostDraftsTableOrderingComposer
|
|||||||
column: $table.lastModified,
|
column: $table.lastModified,
|
||||||
builder: (column) => ColumnOrderings(column),
|
builder: (column) => ColumnOrderings(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ColumnOrderings<String> get postData => $composableBuilder(
|
||||||
|
column: $table.postData,
|
||||||
|
builder: (column) => ColumnOrderings(column),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class $$PostDraftsTableAnnotationComposer
|
class $$PostDraftsTableAnnotationComposer
|
||||||
@@ -1214,13 +1530,32 @@ class $$PostDraftsTableAnnotationComposer
|
|||||||
GeneratedColumn<String> get id =>
|
GeneratedColumn<String> get id =>
|
||||||
$composableBuilder(column: $table.id, builder: (column) => column);
|
$composableBuilder(column: $table.id, builder: (column) => column);
|
||||||
|
|
||||||
GeneratedColumn<String> get post =>
|
GeneratedColumn<String> get title =>
|
||||||
$composableBuilder(column: $table.post, builder: (column) => column);
|
$composableBuilder(column: $table.title, builder: (column) => column);
|
||||||
|
|
||||||
|
GeneratedColumn<String> get description => $composableBuilder(
|
||||||
|
column: $table.description,
|
||||||
|
builder: (column) => column,
|
||||||
|
);
|
||||||
|
|
||||||
|
GeneratedColumn<String> get content =>
|
||||||
|
$composableBuilder(column: $table.content, builder: (column) => column);
|
||||||
|
|
||||||
|
GeneratedColumn<int> get visibility => $composableBuilder(
|
||||||
|
column: $table.visibility,
|
||||||
|
builder: (column) => column,
|
||||||
|
);
|
||||||
|
|
||||||
|
GeneratedColumn<int> get type =>
|
||||||
|
$composableBuilder(column: $table.type, builder: (column) => column);
|
||||||
|
|
||||||
GeneratedColumn<DateTime> get lastModified => $composableBuilder(
|
GeneratedColumn<DateTime> get lastModified => $composableBuilder(
|
||||||
column: $table.lastModified,
|
column: $table.lastModified,
|
||||||
builder: (column) => column,
|
builder: (column) => column,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
GeneratedColumn<String> get postData =>
|
||||||
|
$composableBuilder(column: $table.postData, builder: (column) => column);
|
||||||
}
|
}
|
||||||
|
|
||||||
class $$PostDraftsTableTableManager
|
class $$PostDraftsTableTableManager
|
||||||
@@ -1255,25 +1590,45 @@ class $$PostDraftsTableTableManager
|
|||||||
updateCompanionCallback:
|
updateCompanionCallback:
|
||||||
({
|
({
|
||||||
Value<String> id = const Value.absent(),
|
Value<String> id = const Value.absent(),
|
||||||
Value<String> post = const Value.absent(),
|
Value<String?> title = const Value.absent(),
|
||||||
|
Value<String?> description = const Value.absent(),
|
||||||
|
Value<String?> content = const Value.absent(),
|
||||||
|
Value<int> visibility = const Value.absent(),
|
||||||
|
Value<int> type = const Value.absent(),
|
||||||
Value<DateTime> lastModified = const Value.absent(),
|
Value<DateTime> lastModified = const Value.absent(),
|
||||||
|
Value<String> postData = const Value.absent(),
|
||||||
Value<int> rowid = const Value.absent(),
|
Value<int> rowid = const Value.absent(),
|
||||||
}) => PostDraftsCompanion(
|
}) => PostDraftsCompanion(
|
||||||
id: id,
|
id: id,
|
||||||
post: post,
|
title: title,
|
||||||
|
description: description,
|
||||||
|
content: content,
|
||||||
|
visibility: visibility,
|
||||||
|
type: type,
|
||||||
lastModified: lastModified,
|
lastModified: lastModified,
|
||||||
|
postData: postData,
|
||||||
rowid: rowid,
|
rowid: rowid,
|
||||||
),
|
),
|
||||||
createCompanionCallback:
|
createCompanionCallback:
|
||||||
({
|
({
|
||||||
required String id,
|
required String id,
|
||||||
required String post,
|
Value<String?> title = const Value.absent(),
|
||||||
|
Value<String?> description = const Value.absent(),
|
||||||
|
Value<String?> content = const Value.absent(),
|
||||||
|
Value<int> visibility = const Value.absent(),
|
||||||
|
Value<int> type = const Value.absent(),
|
||||||
required DateTime lastModified,
|
required DateTime lastModified,
|
||||||
|
required String postData,
|
||||||
Value<int> rowid = const Value.absent(),
|
Value<int> rowid = const Value.absent(),
|
||||||
}) => PostDraftsCompanion.insert(
|
}) => PostDraftsCompanion.insert(
|
||||||
id: id,
|
id: id,
|
||||||
post: post,
|
title: title,
|
||||||
|
description: description,
|
||||||
|
content: content,
|
||||||
|
visibility: visibility,
|
||||||
|
type: type,
|
||||||
lastModified: lastModified,
|
lastModified: lastModified,
|
||||||
|
postData: postData,
|
||||||
rowid: rowid,
|
rowid: rowid,
|
||||||
),
|
),
|
||||||
withReferenceMapper:
|
withReferenceMapper:
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ sealed class SnAccount with _$SnAccount {
|
|||||||
required SnAccountProfile profile,
|
required SnAccountProfile profile,
|
||||||
required SnWalletSubscriptionRef? perkSubscription,
|
required SnWalletSubscriptionRef? perkSubscription,
|
||||||
@Default([]) List<SnAccountBadge> badges,
|
@Default([]) List<SnAccountBadge> badges,
|
||||||
|
@Default([]) List<SnContactMethod> contacts,
|
||||||
required DateTime createdAt,
|
required DateTime createdAt,
|
||||||
required DateTime updatedAt,
|
required DateTime updatedAt,
|
||||||
required DateTime? deletedAt,
|
required DateTime? deletedAt,
|
||||||
@@ -135,6 +136,7 @@ sealed class SnContactMethod with _$SnContactMethod {
|
|||||||
required int type,
|
required int type,
|
||||||
required DateTime? verifiedAt,
|
required DateTime? verifiedAt,
|
||||||
required bool isPrimary,
|
required bool isPrimary,
|
||||||
|
required bool isPublic,
|
||||||
required String content,
|
required String content,
|
||||||
required String accountId,
|
required String accountId,
|
||||||
required DateTime createdAt,
|
required DateTime createdAt,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$SnAccount {
|
mixin _$SnAccount {
|
||||||
|
|
||||||
String get id; String get name; String get nick; String get language; String get region; bool get isSuperuser; String? get automatedId; SnAccountProfile get profile; SnWalletSubscriptionRef? get perkSubscription; List<SnAccountBadge> get badges; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
String get id; String get name; String get nick; String get language; String get region; bool get isSuperuser; String? get automatedId; SnAccountProfile get profile; SnWalletSubscriptionRef? get perkSubscription; List<SnAccountBadge> get badges; List<SnContactMethod> get contacts; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||||
/// Create a copy of SnAccount
|
/// Create a copy of SnAccount
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -28,16 +28,16 @@ $SnAccountCopyWith<SnAccount> get copyWith => _$SnAccountCopyWithImpl<SnAccount>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAccount&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.language, language) || other.language == language)&&(identical(other.region, region) || other.region == region)&&(identical(other.isSuperuser, isSuperuser) || other.isSuperuser == isSuperuser)&&(identical(other.automatedId, automatedId) || other.automatedId == automatedId)&&(identical(other.profile, profile) || other.profile == profile)&&(identical(other.perkSubscription, perkSubscription) || other.perkSubscription == perkSubscription)&&const DeepCollectionEquality().equals(other.badges, badges)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAccount&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.language, language) || other.language == language)&&(identical(other.region, region) || other.region == region)&&(identical(other.isSuperuser, isSuperuser) || other.isSuperuser == isSuperuser)&&(identical(other.automatedId, automatedId) || other.automatedId == automatedId)&&(identical(other.profile, profile) || other.profile == profile)&&(identical(other.perkSubscription, perkSubscription) || other.perkSubscription == perkSubscription)&&const DeepCollectionEquality().equals(other.badges, badges)&&const DeepCollectionEquality().equals(other.contacts, contacts)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,id,name,nick,language,region,isSuperuser,automatedId,profile,perkSubscription,const DeepCollectionEquality().hash(badges),createdAt,updatedAt,deletedAt);
|
int get hashCode => Object.hash(runtimeType,id,name,nick,language,region,isSuperuser,automatedId,profile,perkSubscription,const DeepCollectionEquality().hash(badges),const DeepCollectionEquality().hash(contacts),createdAt,updatedAt,deletedAt);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnAccount(id: $id, name: $name, nick: $nick, language: $language, region: $region, isSuperuser: $isSuperuser, automatedId: $automatedId, profile: $profile, perkSubscription: $perkSubscription, badges: $badges, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
return 'SnAccount(id: $id, name: $name, nick: $nick, language: $language, region: $region, isSuperuser: $isSuperuser, automatedId: $automatedId, profile: $profile, perkSubscription: $perkSubscription, badges: $badges, contacts: $contacts, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ abstract mixin class $SnAccountCopyWith<$Res> {
|
|||||||
factory $SnAccountCopyWith(SnAccount value, $Res Function(SnAccount) _then) = _$SnAccountCopyWithImpl;
|
factory $SnAccountCopyWith(SnAccount value, $Res Function(SnAccount) _then) = _$SnAccountCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, List<SnContactMethod> contacts, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ class _$SnAccountCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnAccount
|
/// Create a copy of SnAccount
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? nick = null,Object? language = null,Object? region = null,Object? isSuperuser = null,Object? automatedId = freezed,Object? profile = null,Object? perkSubscription = freezed,Object? badges = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? nick = null,Object? language = null,Object? region = null,Object? isSuperuser = null,Object? automatedId = freezed,Object? profile = null,Object? perkSubscription = freezed,Object? badges = null,Object? contacts = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -77,7 +77,8 @@ as bool,automatedId: freezed == automatedId ? _self.automatedId : automatedId //
|
|||||||
as String?,profile: null == profile ? _self.profile : profile // ignore: cast_nullable_to_non_nullable
|
as String?,profile: null == profile ? _self.profile : profile // ignore: cast_nullable_to_non_nullable
|
||||||
as SnAccountProfile,perkSubscription: freezed == perkSubscription ? _self.perkSubscription : perkSubscription // ignore: cast_nullable_to_non_nullable
|
as SnAccountProfile,perkSubscription: freezed == perkSubscription ? _self.perkSubscription : perkSubscription // ignore: cast_nullable_to_non_nullable
|
||||||
as SnWalletSubscriptionRef?,badges: null == badges ? _self.badges : badges // ignore: cast_nullable_to_non_nullable
|
as SnWalletSubscriptionRef?,badges: null == badges ? _self.badges : badges // ignore: cast_nullable_to_non_nullable
|
||||||
as List<SnAccountBadge>,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
as List<SnAccountBadge>,contacts: null == contacts ? _self.contacts : contacts // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<SnContactMethod>,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime?,
|
as DateTime?,
|
||||||
@@ -183,10 +184,10 @@ return $default(_that);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, List<SnContactMethod> contacts, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnAccount() when $default != null:
|
case _SnAccount() when $default != null:
|
||||||
return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that.isSuperuser,_that.automatedId,_that.profile,_that.perkSubscription,_that.badges,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that.isSuperuser,_that.automatedId,_that.profile,_that.perkSubscription,_that.badges,_that.contacts,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||||
return orElse();
|
return orElse();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -204,10 +205,10 @@ return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, List<SnContactMethod> contacts, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnAccount():
|
case _SnAccount():
|
||||||
return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that.isSuperuser,_that.automatedId,_that.profile,_that.perkSubscription,_that.badges,_that.createdAt,_that.updatedAt,_that.deletedAt);}
|
return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that.isSuperuser,_that.automatedId,_that.profile,_that.perkSubscription,_that.badges,_that.contacts,_that.createdAt,_that.updatedAt,_that.deletedAt);}
|
||||||
}
|
}
|
||||||
/// A variant of `when` that fallback to returning `null`
|
/// A variant of `when` that fallback to returning `null`
|
||||||
///
|
///
|
||||||
@@ -221,10 +222,10 @@ return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, List<SnContactMethod> contacts, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnAccount() when $default != null:
|
case _SnAccount() when $default != null:
|
||||||
return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that.isSuperuser,_that.automatedId,_that.profile,_that.perkSubscription,_that.badges,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that.isSuperuser,_that.automatedId,_that.profile,_that.perkSubscription,_that.badges,_that.contacts,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -236,7 +237,7 @@ return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
|
||||||
class _SnAccount implements SnAccount {
|
class _SnAccount implements SnAccount {
|
||||||
const _SnAccount({required this.id, required this.name, required this.nick, required this.language, this.region = "", required this.isSuperuser, required this.automatedId, required this.profile, required this.perkSubscription, final List<SnAccountBadge> badges = const [], required this.createdAt, required this.updatedAt, required this.deletedAt}): _badges = badges;
|
const _SnAccount({required this.id, required this.name, required this.nick, required this.language, this.region = "", required this.isSuperuser, required this.automatedId, required this.profile, required this.perkSubscription, final List<SnAccountBadge> badges = const [], final List<SnContactMethod> contacts = const [], required this.createdAt, required this.updatedAt, required this.deletedAt}): _badges = badges,_contacts = contacts;
|
||||||
factory _SnAccount.fromJson(Map<String, dynamic> json) => _$SnAccountFromJson(json);
|
factory _SnAccount.fromJson(Map<String, dynamic> json) => _$SnAccountFromJson(json);
|
||||||
|
|
||||||
@override final String id;
|
@override final String id;
|
||||||
@@ -255,6 +256,13 @@ class _SnAccount implements SnAccount {
|
|||||||
return EqualUnmodifiableListView(_badges);
|
return EqualUnmodifiableListView(_badges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final List<SnContactMethod> _contacts;
|
||||||
|
@override@JsonKey() List<SnContactMethod> get contacts {
|
||||||
|
if (_contacts is EqualUnmodifiableListView) return _contacts;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(_contacts);
|
||||||
|
}
|
||||||
|
|
||||||
@override final DateTime createdAt;
|
@override final DateTime createdAt;
|
||||||
@override final DateTime updatedAt;
|
@override final DateTime updatedAt;
|
||||||
@override final DateTime? deletedAt;
|
@override final DateTime? deletedAt;
|
||||||
@@ -272,16 +280,16 @@ Map<String, dynamic> toJson() {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAccount&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.language, language) || other.language == language)&&(identical(other.region, region) || other.region == region)&&(identical(other.isSuperuser, isSuperuser) || other.isSuperuser == isSuperuser)&&(identical(other.automatedId, automatedId) || other.automatedId == automatedId)&&(identical(other.profile, profile) || other.profile == profile)&&(identical(other.perkSubscription, perkSubscription) || other.perkSubscription == perkSubscription)&&const DeepCollectionEquality().equals(other._badges, _badges)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAccount&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.language, language) || other.language == language)&&(identical(other.region, region) || other.region == region)&&(identical(other.isSuperuser, isSuperuser) || other.isSuperuser == isSuperuser)&&(identical(other.automatedId, automatedId) || other.automatedId == automatedId)&&(identical(other.profile, profile) || other.profile == profile)&&(identical(other.perkSubscription, perkSubscription) || other.perkSubscription == perkSubscription)&&const DeepCollectionEquality().equals(other._badges, _badges)&&const DeepCollectionEquality().equals(other._contacts, _contacts)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,id,name,nick,language,region,isSuperuser,automatedId,profile,perkSubscription,const DeepCollectionEquality().hash(_badges),createdAt,updatedAt,deletedAt);
|
int get hashCode => Object.hash(runtimeType,id,name,nick,language,region,isSuperuser,automatedId,profile,perkSubscription,const DeepCollectionEquality().hash(_badges),const DeepCollectionEquality().hash(_contacts),createdAt,updatedAt,deletedAt);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnAccount(id: $id, name: $name, nick: $nick, language: $language, region: $region, isSuperuser: $isSuperuser, automatedId: $automatedId, profile: $profile, perkSubscription: $perkSubscription, badges: $badges, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
return 'SnAccount(id: $id, name: $name, nick: $nick, language: $language, region: $region, isSuperuser: $isSuperuser, automatedId: $automatedId, profile: $profile, perkSubscription: $perkSubscription, badges: $badges, contacts: $contacts, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -292,7 +300,7 @@ abstract mixin class _$SnAccountCopyWith<$Res> implements $SnAccountCopyWith<$Re
|
|||||||
factory _$SnAccountCopyWith(_SnAccount value, $Res Function(_SnAccount) _then) = __$SnAccountCopyWithImpl;
|
factory _$SnAccountCopyWith(_SnAccount value, $Res Function(_SnAccount) _then) = __$SnAccountCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
String id, String name, String nick, String language, String region, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, List<SnContactMethod> contacts, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -309,7 +317,7 @@ class __$SnAccountCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnAccount
|
/// Create a copy of SnAccount
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? nick = null,Object? language = null,Object? region = null,Object? isSuperuser = null,Object? automatedId = freezed,Object? profile = null,Object? perkSubscription = freezed,Object? badges = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? nick = null,Object? language = null,Object? region = null,Object? isSuperuser = null,Object? automatedId = freezed,Object? profile = null,Object? perkSubscription = freezed,Object? badges = null,Object? contacts = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
return _then(_SnAccount(
|
return _then(_SnAccount(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -321,7 +329,8 @@ as bool,automatedId: freezed == automatedId ? _self.automatedId : automatedId //
|
|||||||
as String?,profile: null == profile ? _self.profile : profile // ignore: cast_nullable_to_non_nullable
|
as String?,profile: null == profile ? _self.profile : profile // ignore: cast_nullable_to_non_nullable
|
||||||
as SnAccountProfile,perkSubscription: freezed == perkSubscription ? _self.perkSubscription : perkSubscription // ignore: cast_nullable_to_non_nullable
|
as SnAccountProfile,perkSubscription: freezed == perkSubscription ? _self.perkSubscription : perkSubscription // ignore: cast_nullable_to_non_nullable
|
||||||
as SnWalletSubscriptionRef?,badges: null == badges ? _self._badges : badges // ignore: cast_nullable_to_non_nullable
|
as SnWalletSubscriptionRef?,badges: null == badges ? _self._badges : badges // ignore: cast_nullable_to_non_nullable
|
||||||
as List<SnAccountBadge>,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
as List<SnAccountBadge>,contacts: null == contacts ? _self._contacts : contacts // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<SnContactMethod>,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime?,
|
as DateTime?,
|
||||||
@@ -1627,7 +1636,7 @@ as DateTime?,
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$SnContactMethod {
|
mixin _$SnContactMethod {
|
||||||
|
|
||||||
String get id; int get type; DateTime? get verifiedAt; bool get isPrimary; String get content; String get accountId; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
String get id; int get type; DateTime? get verifiedAt; bool get isPrimary; bool get isPublic; String get content; String get accountId; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||||
/// Create a copy of SnContactMethod
|
/// Create a copy of SnContactMethod
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -1640,16 +1649,16 @@ $SnContactMethodCopyWith<SnContactMethod> get copyWith => _$SnContactMethodCopyW
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnContactMethod&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.verifiedAt, verifiedAt) || other.verifiedAt == verifiedAt)&&(identical(other.isPrimary, isPrimary) || other.isPrimary == isPrimary)&&(identical(other.content, content) || other.content == content)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnContactMethod&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.verifiedAt, verifiedAt) || other.verifiedAt == verifiedAt)&&(identical(other.isPrimary, isPrimary) || other.isPrimary == isPrimary)&&(identical(other.isPublic, isPublic) || other.isPublic == isPublic)&&(identical(other.content, content) || other.content == content)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,id,type,verifiedAt,isPrimary,content,accountId,createdAt,updatedAt,deletedAt);
|
int get hashCode => Object.hash(runtimeType,id,type,verifiedAt,isPrimary,isPublic,content,accountId,createdAt,updatedAt,deletedAt);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnContactMethod(id: $id, type: $type, verifiedAt: $verifiedAt, isPrimary: $isPrimary, content: $content, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
return 'SnContactMethod(id: $id, type: $type, verifiedAt: $verifiedAt, isPrimary: $isPrimary, isPublic: $isPublic, content: $content, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1660,7 +1669,7 @@ abstract mixin class $SnContactMethodCopyWith<$Res> {
|
|||||||
factory $SnContactMethodCopyWith(SnContactMethod value, $Res Function(SnContactMethod) _then) = _$SnContactMethodCopyWithImpl;
|
factory $SnContactMethodCopyWith(SnContactMethod value, $Res Function(SnContactMethod) _then) = _$SnContactMethodCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, int type, DateTime? verifiedAt, bool isPrimary, String content, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
String id, int type, DateTime? verifiedAt, bool isPrimary, bool isPublic, String content, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -1677,12 +1686,13 @@ class _$SnContactMethodCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnContactMethod
|
/// Create a copy of SnContactMethod
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? type = null,Object? verifiedAt = freezed,Object? isPrimary = null,Object? content = null,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? type = null,Object? verifiedAt = freezed,Object? isPrimary = null,Object? isPublic = null,Object? content = null,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||||
as int,verifiedAt: freezed == verifiedAt ? _self.verifiedAt : verifiedAt // ignore: cast_nullable_to_non_nullable
|
as int,verifiedAt: freezed == verifiedAt ? _self.verifiedAt : verifiedAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime?,isPrimary: null == isPrimary ? _self.isPrimary : isPrimary // ignore: cast_nullable_to_non_nullable
|
as DateTime?,isPrimary: null == isPrimary ? _self.isPrimary : isPrimary // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,isPublic: null == isPublic ? _self.isPublic : isPublic // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,content: null == content ? _self.content : content // ignore: cast_nullable_to_non_nullable
|
as bool,content: null == content ? _self.content : content // ignore: cast_nullable_to_non_nullable
|
||||||
as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||||
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -1770,10 +1780,10 @@ return $default(_that);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, int type, DateTime? verifiedAt, bool isPrimary, String content, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, int type, DateTime? verifiedAt, bool isPrimary, bool isPublic, String content, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnContactMethod() when $default != null:
|
case _SnContactMethod() when $default != null:
|
||||||
return $default(_that.id,_that.type,_that.verifiedAt,_that.isPrimary,_that.content,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
return $default(_that.id,_that.type,_that.verifiedAt,_that.isPrimary,_that.isPublic,_that.content,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||||
return orElse();
|
return orElse();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1791,10 +1801,10 @@ return $default(_that.id,_that.type,_that.verifiedAt,_that.isPrimary,_that.conte
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, int type, DateTime? verifiedAt, bool isPrimary, String content, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, int type, DateTime? verifiedAt, bool isPrimary, bool isPublic, String content, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnContactMethod():
|
case _SnContactMethod():
|
||||||
return $default(_that.id,_that.type,_that.verifiedAt,_that.isPrimary,_that.content,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);}
|
return $default(_that.id,_that.type,_that.verifiedAt,_that.isPrimary,_that.isPublic,_that.content,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);}
|
||||||
}
|
}
|
||||||
/// A variant of `when` that fallback to returning `null`
|
/// A variant of `when` that fallback to returning `null`
|
||||||
///
|
///
|
||||||
@@ -1808,10 +1818,10 @@ return $default(_that.id,_that.type,_that.verifiedAt,_that.isPrimary,_that.conte
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, int type, DateTime? verifiedAt, bool isPrimary, String content, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, int type, DateTime? verifiedAt, bool isPrimary, bool isPublic, String content, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnContactMethod() when $default != null:
|
case _SnContactMethod() when $default != null:
|
||||||
return $default(_that.id,_that.type,_that.verifiedAt,_that.isPrimary,_that.content,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
return $default(_that.id,_that.type,_that.verifiedAt,_that.isPrimary,_that.isPublic,_that.content,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1823,13 +1833,14 @@ return $default(_that.id,_that.type,_that.verifiedAt,_that.isPrimary,_that.conte
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
|
||||||
class _SnContactMethod implements SnContactMethod {
|
class _SnContactMethod implements SnContactMethod {
|
||||||
const _SnContactMethod({required this.id, required this.type, required this.verifiedAt, required this.isPrimary, required this.content, required this.accountId, required this.createdAt, required this.updatedAt, required this.deletedAt});
|
const _SnContactMethod({required this.id, required this.type, required this.verifiedAt, required this.isPrimary, required this.isPublic, required this.content, required this.accountId, required this.createdAt, required this.updatedAt, required this.deletedAt});
|
||||||
factory _SnContactMethod.fromJson(Map<String, dynamic> json) => _$SnContactMethodFromJson(json);
|
factory _SnContactMethod.fromJson(Map<String, dynamic> json) => _$SnContactMethodFromJson(json);
|
||||||
|
|
||||||
@override final String id;
|
@override final String id;
|
||||||
@override final int type;
|
@override final int type;
|
||||||
@override final DateTime? verifiedAt;
|
@override final DateTime? verifiedAt;
|
||||||
@override final bool isPrimary;
|
@override final bool isPrimary;
|
||||||
|
@override final bool isPublic;
|
||||||
@override final String content;
|
@override final String content;
|
||||||
@override final String accountId;
|
@override final String accountId;
|
||||||
@override final DateTime createdAt;
|
@override final DateTime createdAt;
|
||||||
@@ -1849,16 +1860,16 @@ Map<String, dynamic> toJson() {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnContactMethod&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.verifiedAt, verifiedAt) || other.verifiedAt == verifiedAt)&&(identical(other.isPrimary, isPrimary) || other.isPrimary == isPrimary)&&(identical(other.content, content) || other.content == content)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnContactMethod&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.verifiedAt, verifiedAt) || other.verifiedAt == verifiedAt)&&(identical(other.isPrimary, isPrimary) || other.isPrimary == isPrimary)&&(identical(other.isPublic, isPublic) || other.isPublic == isPublic)&&(identical(other.content, content) || other.content == content)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,id,type,verifiedAt,isPrimary,content,accountId,createdAt,updatedAt,deletedAt);
|
int get hashCode => Object.hash(runtimeType,id,type,verifiedAt,isPrimary,isPublic,content,accountId,createdAt,updatedAt,deletedAt);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnContactMethod(id: $id, type: $type, verifiedAt: $verifiedAt, isPrimary: $isPrimary, content: $content, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
return 'SnContactMethod(id: $id, type: $type, verifiedAt: $verifiedAt, isPrimary: $isPrimary, isPublic: $isPublic, content: $content, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1869,7 +1880,7 @@ abstract mixin class _$SnContactMethodCopyWith<$Res> implements $SnContactMethod
|
|||||||
factory _$SnContactMethodCopyWith(_SnContactMethod value, $Res Function(_SnContactMethod) _then) = __$SnContactMethodCopyWithImpl;
|
factory _$SnContactMethodCopyWith(_SnContactMethod value, $Res Function(_SnContactMethod) _then) = __$SnContactMethodCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, int type, DateTime? verifiedAt, bool isPrimary, String content, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
String id, int type, DateTime? verifiedAt, bool isPrimary, bool isPublic, String content, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -1886,12 +1897,13 @@ class __$SnContactMethodCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnContactMethod
|
/// Create a copy of SnContactMethod
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? type = null,Object? verifiedAt = freezed,Object? isPrimary = null,Object? content = null,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? type = null,Object? verifiedAt = freezed,Object? isPrimary = null,Object? isPublic = null,Object? content = null,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
return _then(_SnContactMethod(
|
return _then(_SnContactMethod(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||||
as int,verifiedAt: freezed == verifiedAt ? _self.verifiedAt : verifiedAt // ignore: cast_nullable_to_non_nullable
|
as int,verifiedAt: freezed == verifiedAt ? _self.verifiedAt : verifiedAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime?,isPrimary: null == isPrimary ? _self.isPrimary : isPrimary // ignore: cast_nullable_to_non_nullable
|
as DateTime?,isPrimary: null == isPrimary ? _self.isPrimary : isPrimary // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,isPublic: null == isPublic ? _self.isPublic : isPublic // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,content: null == content ? _self.content : content // ignore: cast_nullable_to_non_nullable
|
as bool,content: null == content ? _self.content : content // ignore: cast_nullable_to_non_nullable
|
||||||
as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||||
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ _SnAccount _$SnAccountFromJson(Map<String, dynamic> json) => _SnAccount(
|
|||||||
?.map((e) => SnAccountBadge.fromJson(e as Map<String, dynamic>))
|
?.map((e) => SnAccountBadge.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
const [],
|
const [],
|
||||||
|
contacts:
|
||||||
|
(json['contacts'] as List<dynamic>?)
|
||||||
|
?.map((e) => SnContactMethod.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList() ??
|
||||||
|
const [],
|
||||||
createdAt: DateTime.parse(json['created_at'] as String),
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
deletedAt:
|
deletedAt:
|
||||||
@@ -46,6 +51,7 @@ Map<String, dynamic> _$SnAccountToJson(_SnAccount instance) =>
|
|||||||
'profile': instance.profile.toJson(),
|
'profile': instance.profile.toJson(),
|
||||||
'perk_subscription': instance.perkSubscription?.toJson(),
|
'perk_subscription': instance.perkSubscription?.toJson(),
|
||||||
'badges': instance.badges.map((e) => e.toJson()).toList(),
|
'badges': instance.badges.map((e) => e.toJson()).toList(),
|
||||||
|
'contacts': instance.contacts.map((e) => e.toJson()).toList(),
|
||||||
'created_at': instance.createdAt.toIso8601String(),
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
'updated_at': instance.updatedAt.toIso8601String(),
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
'deleted_at': instance.deletedAt?.toIso8601String(),
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
@@ -229,6 +235,7 @@ _SnContactMethod _$SnContactMethodFromJson(Map<String, dynamic> json) =>
|
|||||||
? null
|
? null
|
||||||
: DateTime.parse(json['verified_at'] as String),
|
: DateTime.parse(json['verified_at'] as String),
|
||||||
isPrimary: json['is_primary'] as bool,
|
isPrimary: json['is_primary'] as bool,
|
||||||
|
isPublic: json['is_public'] as bool,
|
||||||
content: json['content'] as String,
|
content: json['content'] as String,
|
||||||
accountId: json['account_id'] as String,
|
accountId: json['account_id'] as String,
|
||||||
createdAt: DateTime.parse(json['created_at'] as String),
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
@@ -245,6 +252,7 @@ Map<String, dynamic> _$SnContactMethodToJson(_SnContactMethod instance) =>
|
|||||||
'type': instance.type,
|
'type': instance.type,
|
||||||
'verified_at': instance.verifiedAt?.toIso8601String(),
|
'verified_at': instance.verifiedAt?.toIso8601String(),
|
||||||
'is_primary': instance.isPrimary,
|
'is_primary': instance.isPrimary,
|
||||||
|
'is_public': instance.isPublic,
|
||||||
'content': instance.content,
|
'content': instance.content,
|
||||||
'account_id': instance.accountId,
|
'account_id': instance.accountId,
|
||||||
'created_at': instance.createdAt.toIso8601String(),
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ sealed class SnPost with _$SnPost {
|
|||||||
String? slug,
|
String? slug,
|
||||||
@Default(0) int type,
|
@Default(0) int type,
|
||||||
Map<String, dynamic>? meta,
|
Map<String, dynamic>? meta,
|
||||||
|
SnPostEmbedView? embedView,
|
||||||
@Default(0) int viewsUnique,
|
@Default(0) int viewsUnique,
|
||||||
@Default(0) int viewsTotal,
|
@Default(0) int viewsTotal,
|
||||||
@Default(0) int upvotes,
|
@Default(0) int upvotes,
|
||||||
@@ -105,3 +106,20 @@ const Map<String, ReactInfo> kReactionTemplates = {
|
|||||||
'pray': ReactInfo(icon: '🙏', attitude: 0),
|
'pray': ReactInfo(icon: '🙏', attitude: 0),
|
||||||
'heart': ReactInfo(icon: '❤️', attitude: 0),
|
'heart': ReactInfo(icon: '❤️', attitude: 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum PostEmbedViewRenderer {
|
||||||
|
@JsonValue(0)
|
||||||
|
webView,
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class SnPostEmbedView with _$SnPostEmbedView {
|
||||||
|
const factory SnPostEmbedView({
|
||||||
|
required String uri,
|
||||||
|
double? aspectRatio,
|
||||||
|
@Default(PostEmbedViewRenderer.webView) PostEmbedViewRenderer renderer,
|
||||||
|
}) = _SnPostEmbedView;
|
||||||
|
|
||||||
|
factory SnPostEmbedView.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnPostEmbedViewFromJson(json);
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$SnPost {
|
mixin _$SnPost {
|
||||||
|
|
||||||
String get id; String? get title; String? get description; String? get language; DateTime? get editedAt; DateTime? get publishedAt; int get visibility; String? get content; String? get slug; int get type; Map<String, dynamic>? get meta; int get viewsUnique; int get viewsTotal; int get upvotes; int get downvotes; int get repliesCount; int? get pinMode; String? get threadedPostId; SnPost? get threadedPost; String? get repliedPostId; SnPost? get repliedPost; String? get forwardedPostId; SnPost? get forwardedPost; String? get realmId; SnRealm? get realm; List<SnCloudFile> get attachments; SnPublisher get publisher; Map<String, int> get reactionsCount; Map<String, bool> get reactionsMade; List<dynamic> get reactions; List<SnPostTag> get tags; List<SnPostCategory> get categories; List<dynamic> get collections; DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; bool get isTruncated;
|
String get id; String? get title; String? get description; String? get language; DateTime? get editedAt; DateTime? get publishedAt; int get visibility; String? get content; String? get slug; int get type; Map<String, dynamic>? get meta; SnPostEmbedView? get embedView; int get viewsUnique; int get viewsTotal; int get upvotes; int get downvotes; int get repliesCount; int? get pinMode; String? get threadedPostId; SnPost? get threadedPost; String? get repliedPostId; SnPost? get repliedPost; String? get forwardedPostId; SnPost? get forwardedPost; String? get realmId; SnRealm? get realm; List<SnCloudFile> get attachments; SnPublisher get publisher; Map<String, int> get reactionsCount; Map<String, bool> get reactionsMade; List<dynamic> get reactions; List<SnPostTag> get tags; List<SnPostCategory> get categories; List<dynamic> get collections; DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; bool get isTruncated;
|
||||||
/// Create a copy of SnPost
|
/// Create a copy of SnPost
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -28,16 +28,16 @@ $SnPostCopyWith<SnPost> get copyWith => _$SnPostCopyWithImpl<SnPost>(this as SnP
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.meta, meta)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.pinMode, pinMode) || other.pinMode == pinMode)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&const DeepCollectionEquality().equals(other.attachments, attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other.reactionsCount, reactionsCount)&&const DeepCollectionEquality().equals(other.reactionsMade, reactionsMade)&&const DeepCollectionEquality().equals(other.reactions, reactions)&&const DeepCollectionEquality().equals(other.tags, tags)&&const DeepCollectionEquality().equals(other.categories, categories)&&const DeepCollectionEquality().equals(other.collections, collections)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.meta, meta)&&(identical(other.embedView, embedView) || other.embedView == embedView)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.pinMode, pinMode) || other.pinMode == pinMode)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&const DeepCollectionEquality().equals(other.attachments, attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other.reactionsCount, reactionsCount)&&const DeepCollectionEquality().equals(other.reactionsMade, reactionsMade)&&const DeepCollectionEquality().equals(other.reactions, reactions)&&const DeepCollectionEquality().equals(other.tags, tags)&&const DeepCollectionEquality().equals(other.categories, categories)&&const DeepCollectionEquality().equals(other.collections, collections)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,slug,type,const DeepCollectionEquality().hash(meta),viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,pinMode,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,realmId,realm,const DeepCollectionEquality().hash(attachments),publisher,const DeepCollectionEquality().hash(reactionsCount),const DeepCollectionEquality().hash(reactionsMade),const DeepCollectionEquality().hash(reactions),const DeepCollectionEquality().hash(tags),const DeepCollectionEquality().hash(categories),const DeepCollectionEquality().hash(collections),createdAt,updatedAt,deletedAt,isTruncated]);
|
int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,slug,type,const DeepCollectionEquality().hash(meta),embedView,viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,pinMode,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,realmId,realm,const DeepCollectionEquality().hash(attachments),publisher,const DeepCollectionEquality().hash(reactionsCount),const DeepCollectionEquality().hash(reactionsMade),const DeepCollectionEquality().hash(reactions),const DeepCollectionEquality().hash(tags),const DeepCollectionEquality().hash(categories),const DeepCollectionEquality().hash(collections),createdAt,updatedAt,deletedAt,isTruncated]);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, slug: $slug, type: $type, meta: $meta, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, pinMode: $pinMode, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, realmId: $realmId, realm: $realm, attachments: $attachments, publisher: $publisher, reactionsCount: $reactionsCount, reactionsMade: $reactionsMade, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, isTruncated: $isTruncated)';
|
return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, slug: $slug, type: $type, meta: $meta, embedView: $embedView, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, pinMode: $pinMode, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, realmId: $realmId, realm: $realm, attachments: $attachments, publisher: $publisher, reactionsCount: $reactionsCount, reactionsMade: $reactionsMade, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, isTruncated: $isTruncated)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -48,11 +48,11 @@ abstract mixin class $SnPostCopyWith<$Res> {
|
|||||||
factory $SnPostCopyWith(SnPost value, $Res Function(SnPost) _then) = _$SnPostCopyWithImpl;
|
factory $SnPostCopyWith(SnPost value, $Res Function(SnPost) _then) = _$SnPostCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated
|
String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, SnPostEmbedView? embedView, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
$SnPostCopyWith<$Res>? get threadedPost;$SnPostCopyWith<$Res>? get repliedPost;$SnPostCopyWith<$Res>? get forwardedPost;$SnRealmCopyWith<$Res>? get realm;$SnPublisherCopyWith<$Res> get publisher;
|
$SnPostEmbedViewCopyWith<$Res>? get embedView;$SnPostCopyWith<$Res>? get threadedPost;$SnPostCopyWith<$Res>? get repliedPost;$SnPostCopyWith<$Res>? get forwardedPost;$SnRealmCopyWith<$Res>? get realm;$SnPublisherCopyWith<$Res> get publisher;
|
||||||
|
|
||||||
}
|
}
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -65,7 +65,7 @@ class _$SnPostCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnPost
|
/// Create a copy of SnPost
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? slug = freezed,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? pinMode = freezed,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? realmId = freezed,Object? realm = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactionsMade = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? isTruncated = null,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? slug = freezed,Object? type = null,Object? meta = freezed,Object? embedView = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? pinMode = freezed,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? realmId = freezed,Object? realm = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactionsMade = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? isTruncated = null,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
|
as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -78,7 +78,8 @@ as int,content: freezed == content ? _self.content : content // ignore: cast_nul
|
|||||||
as String?,slug: freezed == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
as String?,slug: freezed == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
as String?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||||
as int,meta: freezed == meta ? _self.meta : meta // ignore: cast_nullable_to_non_nullable
|
as int,meta: freezed == meta ? _self.meta : meta // ignore: cast_nullable_to_non_nullable
|
||||||
as Map<String, dynamic>?,viewsUnique: null == viewsUnique ? _self.viewsUnique : viewsUnique // ignore: cast_nullable_to_non_nullable
|
as Map<String, dynamic>?,embedView: freezed == embedView ? _self.embedView : embedView // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnPostEmbedView?,viewsUnique: null == viewsUnique ? _self.viewsUnique : viewsUnique // ignore: cast_nullable_to_non_nullable
|
||||||
as int,viewsTotal: null == viewsTotal ? _self.viewsTotal : viewsTotal // ignore: cast_nullable_to_non_nullable
|
as int,viewsTotal: null == viewsTotal ? _self.viewsTotal : viewsTotal // ignore: cast_nullable_to_non_nullable
|
||||||
as int,upvotes: null == upvotes ? _self.upvotes : upvotes // ignore: cast_nullable_to_non_nullable
|
as int,upvotes: null == upvotes ? _self.upvotes : upvotes // ignore: cast_nullable_to_non_nullable
|
||||||
as int,downvotes: null == downvotes ? _self.downvotes : downvotes // ignore: cast_nullable_to_non_nullable
|
as int,downvotes: null == downvotes ? _self.downvotes : downvotes // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -111,6 +112,18 @@ as bool,
|
|||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnPostEmbedViewCopyWith<$Res>? get embedView {
|
||||||
|
if (_self.embedView == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnPostEmbedViewCopyWith<$Res>(_self.embedView!, (value) {
|
||||||
|
return _then(_self.copyWith(embedView: value));
|
||||||
|
});
|
||||||
|
}/// Create a copy of SnPost
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
$SnPostCopyWith<$Res>? get threadedPost {
|
$SnPostCopyWith<$Res>? get threadedPost {
|
||||||
if (_self.threadedPost == null) {
|
if (_self.threadedPost == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -243,10 +256,10 @@ return $default(_that);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated)? $default,{required TResult orElse(),}) {final _that = this;
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, SnPostEmbedView? embedView, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnPost() when $default != null:
|
case _SnPost() when $default != null:
|
||||||
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.pinMode,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);case _:
|
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.embedView,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.pinMode,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);case _:
|
||||||
return orElse();
|
return orElse();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -264,10 +277,10 @@ return $default(_that.id,_that.title,_that.description,_that.language,_that.edit
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated) $default,) {final _that = this;
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, SnPostEmbedView? embedView, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated) $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnPost():
|
case _SnPost():
|
||||||
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.pinMode,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);}
|
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.embedView,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.pinMode,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);}
|
||||||
}
|
}
|
||||||
/// A variant of `when` that fallback to returning `null`
|
/// A variant of `when` that fallback to returning `null`
|
||||||
///
|
///
|
||||||
@@ -281,10 +294,10 @@ return $default(_that.id,_that.title,_that.description,_that.language,_that.edit
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated)? $default,) {final _that = this;
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, SnPostEmbedView? embedView, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated)? $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnPost() when $default != null:
|
case _SnPost() when $default != null:
|
||||||
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.pinMode,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);case _:
|
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.embedView,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.pinMode,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);case _:
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -296,7 +309,7 @@ return $default(_that.id,_that.title,_that.description,_that.language,_that.edit
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
|
||||||
class _SnPost implements SnPost {
|
class _SnPost implements SnPost {
|
||||||
const _SnPost({required this.id, this.title, this.description, this.language, this.editedAt, this.publishedAt = null, this.visibility = 0, this.content, this.slug, this.type = 0, final Map<String, dynamic>? meta, this.viewsUnique = 0, this.viewsTotal = 0, this.upvotes = 0, this.downvotes = 0, this.repliesCount = 0, this.pinMode, this.threadedPostId, this.threadedPost, this.repliedPostId, this.repliedPost, this.forwardedPostId, this.forwardedPost, this.realmId, this.realm, final List<SnCloudFile> attachments = const [], required this.publisher, final Map<String, int> reactionsCount = const {}, final Map<String, bool> reactionsMade = const {}, final List<dynamic> reactions = const [], final List<SnPostTag> tags = const [], final List<SnPostCategory> categories = const [], final List<dynamic> collections = const [], this.createdAt = null, this.updatedAt = null, this.deletedAt, this.isTruncated = false}): _meta = meta,_attachments = attachments,_reactionsCount = reactionsCount,_reactionsMade = reactionsMade,_reactions = reactions,_tags = tags,_categories = categories,_collections = collections;
|
const _SnPost({required this.id, this.title, this.description, this.language, this.editedAt, this.publishedAt = null, this.visibility = 0, this.content, this.slug, this.type = 0, final Map<String, dynamic>? meta, this.embedView, this.viewsUnique = 0, this.viewsTotal = 0, this.upvotes = 0, this.downvotes = 0, this.repliesCount = 0, this.pinMode, this.threadedPostId, this.threadedPost, this.repliedPostId, this.repliedPost, this.forwardedPostId, this.forwardedPost, this.realmId, this.realm, final List<SnCloudFile> attachments = const [], required this.publisher, final Map<String, int> reactionsCount = const {}, final Map<String, bool> reactionsMade = const {}, final List<dynamic> reactions = const [], final List<SnPostTag> tags = const [], final List<SnPostCategory> categories = const [], final List<dynamic> collections = const [], this.createdAt = null, this.updatedAt = null, this.deletedAt, this.isTruncated = false}): _meta = meta,_attachments = attachments,_reactionsCount = reactionsCount,_reactionsMade = reactionsMade,_reactions = reactions,_tags = tags,_categories = categories,_collections = collections;
|
||||||
factory _SnPost.fromJson(Map<String, dynamic> json) => _$SnPostFromJson(json);
|
factory _SnPost.fromJson(Map<String, dynamic> json) => _$SnPostFromJson(json);
|
||||||
|
|
||||||
@override final String id;
|
@override final String id;
|
||||||
@@ -318,6 +331,7 @@ class _SnPost implements SnPost {
|
|||||||
return EqualUnmodifiableMapView(value);
|
return EqualUnmodifiableMapView(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override final SnPostEmbedView? embedView;
|
||||||
@override@JsonKey() final int viewsUnique;
|
@override@JsonKey() final int viewsUnique;
|
||||||
@override@JsonKey() final int viewsTotal;
|
@override@JsonKey() final int viewsTotal;
|
||||||
@override@JsonKey() final int upvotes;
|
@override@JsonKey() final int upvotes;
|
||||||
@@ -400,16 +414,16 @@ Map<String, dynamic> toJson() {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._meta, _meta)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.pinMode, pinMode) || other.pinMode == pinMode)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&const DeepCollectionEquality().equals(other._attachments, _attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other._reactionsCount, _reactionsCount)&&const DeepCollectionEquality().equals(other._reactionsMade, _reactionsMade)&&const DeepCollectionEquality().equals(other._reactions, _reactions)&&const DeepCollectionEquality().equals(other._tags, _tags)&&const DeepCollectionEquality().equals(other._categories, _categories)&&const DeepCollectionEquality().equals(other._collections, _collections)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._meta, _meta)&&(identical(other.embedView, embedView) || other.embedView == embedView)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.pinMode, pinMode) || other.pinMode == pinMode)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&const DeepCollectionEquality().equals(other._attachments, _attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other._reactionsCount, _reactionsCount)&&const DeepCollectionEquality().equals(other._reactionsMade, _reactionsMade)&&const DeepCollectionEquality().equals(other._reactions, _reactions)&&const DeepCollectionEquality().equals(other._tags, _tags)&&const DeepCollectionEquality().equals(other._categories, _categories)&&const DeepCollectionEquality().equals(other._collections, _collections)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,slug,type,const DeepCollectionEquality().hash(_meta),viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,pinMode,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,realmId,realm,const DeepCollectionEquality().hash(_attachments),publisher,const DeepCollectionEquality().hash(_reactionsCount),const DeepCollectionEquality().hash(_reactionsMade),const DeepCollectionEquality().hash(_reactions),const DeepCollectionEquality().hash(_tags),const DeepCollectionEquality().hash(_categories),const DeepCollectionEquality().hash(_collections),createdAt,updatedAt,deletedAt,isTruncated]);
|
int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,slug,type,const DeepCollectionEquality().hash(_meta),embedView,viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,pinMode,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,realmId,realm,const DeepCollectionEquality().hash(_attachments),publisher,const DeepCollectionEquality().hash(_reactionsCount),const DeepCollectionEquality().hash(_reactionsMade),const DeepCollectionEquality().hash(_reactions),const DeepCollectionEquality().hash(_tags),const DeepCollectionEquality().hash(_categories),const DeepCollectionEquality().hash(_collections),createdAt,updatedAt,deletedAt,isTruncated]);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, slug: $slug, type: $type, meta: $meta, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, pinMode: $pinMode, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, realmId: $realmId, realm: $realm, attachments: $attachments, publisher: $publisher, reactionsCount: $reactionsCount, reactionsMade: $reactionsMade, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, isTruncated: $isTruncated)';
|
return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, slug: $slug, type: $type, meta: $meta, embedView: $embedView, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, pinMode: $pinMode, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, realmId: $realmId, realm: $realm, attachments: $attachments, publisher: $publisher, reactionsCount: $reactionsCount, reactionsMade: $reactionsMade, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, isTruncated: $isTruncated)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -420,11 +434,11 @@ abstract mixin class _$SnPostCopyWith<$Res> implements $SnPostCopyWith<$Res> {
|
|||||||
factory _$SnPostCopyWith(_SnPost value, $Res Function(_SnPost) _then) = __$SnPostCopyWithImpl;
|
factory _$SnPostCopyWith(_SnPost value, $Res Function(_SnPost) _then) = __$SnPostCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated
|
String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, SnPostEmbedView? embedView, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@override $SnPostCopyWith<$Res>? get threadedPost;@override $SnPostCopyWith<$Res>? get repliedPost;@override $SnPostCopyWith<$Res>? get forwardedPost;@override $SnRealmCopyWith<$Res>? get realm;@override $SnPublisherCopyWith<$Res> get publisher;
|
@override $SnPostEmbedViewCopyWith<$Res>? get embedView;@override $SnPostCopyWith<$Res>? get threadedPost;@override $SnPostCopyWith<$Res>? get repliedPost;@override $SnPostCopyWith<$Res>? get forwardedPost;@override $SnRealmCopyWith<$Res>? get realm;@override $SnPublisherCopyWith<$Res> get publisher;
|
||||||
|
|
||||||
}
|
}
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -437,7 +451,7 @@ class __$SnPostCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnPost
|
/// Create a copy of SnPost
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? slug = freezed,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? pinMode = freezed,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? realmId = freezed,Object? realm = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactionsMade = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? isTruncated = null,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? slug = freezed,Object? type = null,Object? meta = freezed,Object? embedView = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? pinMode = freezed,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? realmId = freezed,Object? realm = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactionsMade = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? isTruncated = null,}) {
|
||||||
return _then(_SnPost(
|
return _then(_SnPost(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
|
as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -450,7 +464,8 @@ as int,content: freezed == content ? _self.content : content // ignore: cast_nul
|
|||||||
as String?,slug: freezed == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
as String?,slug: freezed == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
as String?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||||
as int,meta: freezed == meta ? _self._meta : meta // ignore: cast_nullable_to_non_nullable
|
as int,meta: freezed == meta ? _self._meta : meta // ignore: cast_nullable_to_non_nullable
|
||||||
as Map<String, dynamic>?,viewsUnique: null == viewsUnique ? _self.viewsUnique : viewsUnique // ignore: cast_nullable_to_non_nullable
|
as Map<String, dynamic>?,embedView: freezed == embedView ? _self.embedView : embedView // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnPostEmbedView?,viewsUnique: null == viewsUnique ? _self.viewsUnique : viewsUnique // ignore: cast_nullable_to_non_nullable
|
||||||
as int,viewsTotal: null == viewsTotal ? _self.viewsTotal : viewsTotal // ignore: cast_nullable_to_non_nullable
|
as int,viewsTotal: null == viewsTotal ? _self.viewsTotal : viewsTotal // ignore: cast_nullable_to_non_nullable
|
||||||
as int,upvotes: null == upvotes ? _self.upvotes : upvotes // ignore: cast_nullable_to_non_nullable
|
as int,upvotes: null == upvotes ? _self.upvotes : upvotes // ignore: cast_nullable_to_non_nullable
|
||||||
as int,downvotes: null == downvotes ? _self.downvotes : downvotes // ignore: cast_nullable_to_non_nullable
|
as int,downvotes: null == downvotes ? _self.downvotes : downvotes // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -484,6 +499,18 @@ as bool,
|
|||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnPostEmbedViewCopyWith<$Res>? get embedView {
|
||||||
|
if (_self.embedView == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnPostEmbedViewCopyWith<$Res>(_self.embedView!, (value) {
|
||||||
|
return _then(_self.copyWith(embedView: value));
|
||||||
|
});
|
||||||
|
}/// Create a copy of SnPost
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
$SnPostCopyWith<$Res>? get threadedPost {
|
$SnPostCopyWith<$Res>? get threadedPost {
|
||||||
if (_self.threadedPost == null) {
|
if (_self.threadedPost == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -1324,6 +1351,269 @@ as int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnPostEmbedView {
|
||||||
|
|
||||||
|
String get uri; double? get aspectRatio; PostEmbedViewRenderer get renderer;
|
||||||
|
/// Create a copy of SnPostEmbedView
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnPostEmbedViewCopyWith<SnPostEmbedView> get copyWith => _$SnPostEmbedViewCopyWithImpl<SnPostEmbedView>(this as SnPostEmbedView, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnPostEmbedView to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPostEmbedView&&(identical(other.uri, uri) || other.uri == uri)&&(identical(other.aspectRatio, aspectRatio) || other.aspectRatio == aspectRatio)&&(identical(other.renderer, renderer) || other.renderer == renderer));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,uri,aspectRatio,renderer);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnPostEmbedView(uri: $uri, aspectRatio: $aspectRatio, renderer: $renderer)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $SnPostEmbedViewCopyWith<$Res> {
|
||||||
|
factory $SnPostEmbedViewCopyWith(SnPostEmbedView value, $Res Function(SnPostEmbedView) _then) = _$SnPostEmbedViewCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String uri, double? aspectRatio, PostEmbedViewRenderer renderer
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnPostEmbedViewCopyWithImpl<$Res>
|
||||||
|
implements $SnPostEmbedViewCopyWith<$Res> {
|
||||||
|
_$SnPostEmbedViewCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnPostEmbedView _self;
|
||||||
|
final $Res Function(SnPostEmbedView) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnPostEmbedView
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? uri = null,Object? aspectRatio = freezed,Object? renderer = null,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
uri: null == uri ? _self.uri : uri // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,aspectRatio: freezed == aspectRatio ? _self.aspectRatio : aspectRatio // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double?,renderer: null == renderer ? _self.renderer : renderer // ignore: cast_nullable_to_non_nullable
|
||||||
|
as PostEmbedViewRenderer,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [SnPostEmbedView].
|
||||||
|
extension SnPostEmbedViewPatterns on SnPostEmbedView {
|
||||||
|
/// A variant of `map` that fallback to returning `orElse`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnPostEmbedView value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnPostEmbedView() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// Callbacks receives the raw object, upcasted.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case final Subclass2 value:
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnPostEmbedView value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnPostEmbedView():
|
||||||
|
return $default(_that);}
|
||||||
|
}
|
||||||
|
/// A variant of `map` that fallback to returning `null`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnPostEmbedView value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnPostEmbedView() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to an `orElse` callback.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String uri, double? aspectRatio, PostEmbedViewRenderer renderer)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnPostEmbedView() when $default != null:
|
||||||
|
return $default(_that.uri,_that.aspectRatio,_that.renderer);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// As opposed to `map`, this offers destructuring.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case Subclass2(:final field2):
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String uri, double? aspectRatio, PostEmbedViewRenderer renderer) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnPostEmbedView():
|
||||||
|
return $default(_that.uri,_that.aspectRatio,_that.renderer);}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to returning `null`
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String uri, double? aspectRatio, PostEmbedViewRenderer renderer)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnPostEmbedView() when $default != null:
|
||||||
|
return $default(_that.uri,_that.aspectRatio,_that.renderer);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _SnPostEmbedView implements SnPostEmbedView {
|
||||||
|
const _SnPostEmbedView({required this.uri, this.aspectRatio, this.renderer = PostEmbedViewRenderer.webView});
|
||||||
|
factory _SnPostEmbedView.fromJson(Map<String, dynamic> json) => _$SnPostEmbedViewFromJson(json);
|
||||||
|
|
||||||
|
@override final String uri;
|
||||||
|
@override final double? aspectRatio;
|
||||||
|
@override@JsonKey() final PostEmbedViewRenderer renderer;
|
||||||
|
|
||||||
|
/// Create a copy of SnPostEmbedView
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnPostEmbedViewCopyWith<_SnPostEmbedView> get copyWith => __$SnPostEmbedViewCopyWithImpl<_SnPostEmbedView>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnPostEmbedViewToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPostEmbedView&&(identical(other.uri, uri) || other.uri == uri)&&(identical(other.aspectRatio, aspectRatio) || other.aspectRatio == aspectRatio)&&(identical(other.renderer, renderer) || other.renderer == renderer));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,uri,aspectRatio,renderer);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnPostEmbedView(uri: $uri, aspectRatio: $aspectRatio, renderer: $renderer)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnPostEmbedViewCopyWith<$Res> implements $SnPostEmbedViewCopyWith<$Res> {
|
||||||
|
factory _$SnPostEmbedViewCopyWith(_SnPostEmbedView value, $Res Function(_SnPostEmbedView) _then) = __$SnPostEmbedViewCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String uri, double? aspectRatio, PostEmbedViewRenderer renderer
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnPostEmbedViewCopyWithImpl<$Res>
|
||||||
|
implements _$SnPostEmbedViewCopyWith<$Res> {
|
||||||
|
__$SnPostEmbedViewCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnPostEmbedView _self;
|
||||||
|
final $Res Function(_SnPostEmbedView) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnPostEmbedView
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? uri = null,Object? aspectRatio = freezed,Object? renderer = null,}) {
|
||||||
|
return _then(_SnPostEmbedView(
|
||||||
|
uri: null == uri ? _self.uri : uri // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,aspectRatio: freezed == aspectRatio ? _self.aspectRatio : aspectRatio // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double?,renderer: null == renderer ? _self.renderer : renderer // ignore: cast_nullable_to_non_nullable
|
||||||
|
as PostEmbedViewRenderer,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// dart format on
|
// dart format on
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ _SnPost _$SnPostFromJson(Map<String, dynamic> json) => _SnPost(
|
|||||||
slug: json['slug'] as String?,
|
slug: json['slug'] as String?,
|
||||||
type: (json['type'] as num?)?.toInt() ?? 0,
|
type: (json['type'] as num?)?.toInt() ?? 0,
|
||||||
meta: json['meta'] as Map<String, dynamic>?,
|
meta: json['meta'] as Map<String, dynamic>?,
|
||||||
|
embedView:
|
||||||
|
json['embed_view'] == null
|
||||||
|
? null
|
||||||
|
: SnPostEmbedView.fromJson(
|
||||||
|
json['embed_view'] as Map<String, dynamic>,
|
||||||
|
),
|
||||||
viewsUnique: (json['views_unique'] as num?)?.toInt() ?? 0,
|
viewsUnique: (json['views_unique'] as num?)?.toInt() ?? 0,
|
||||||
viewsTotal: (json['views_total'] as num?)?.toInt() ?? 0,
|
viewsTotal: (json['views_total'] as num?)?.toInt() ?? 0,
|
||||||
upvotes: (json['upvotes'] as num?)?.toInt() ?? 0,
|
upvotes: (json['upvotes'] as num?)?.toInt() ?? 0,
|
||||||
@@ -105,6 +111,7 @@ Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
|
|||||||
'slug': instance.slug,
|
'slug': instance.slug,
|
||||||
'type': instance.type,
|
'type': instance.type,
|
||||||
'meta': instance.meta,
|
'meta': instance.meta,
|
||||||
|
'embed_view': instance.embedView?.toJson(),
|
||||||
'views_unique': instance.viewsUnique,
|
'views_unique': instance.viewsUnique,
|
||||||
'views_total': instance.viewsTotal,
|
'views_total': instance.viewsTotal,
|
||||||
'upvotes': instance.upvotes,
|
'upvotes': instance.upvotes,
|
||||||
@@ -166,3 +173,24 @@ Map<String, dynamic> _$SnSubscriptionStatusToJson(
|
|||||||
'publisher_id': instance.publisherId,
|
'publisher_id': instance.publisherId,
|
||||||
'publisher_name': instance.publisherName,
|
'publisher_name': instance.publisherName,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_SnPostEmbedView _$SnPostEmbedViewFromJson(Map<String, dynamic> json) =>
|
||||||
|
_SnPostEmbedView(
|
||||||
|
uri: json['uri'] as String,
|
||||||
|
aspectRatio: (json['aspect_ratio'] as num?)?.toDouble(),
|
||||||
|
renderer:
|
||||||
|
$enumDecodeNullable(
|
||||||
|
_$PostEmbedViewRendererEnumMap,
|
||||||
|
json['renderer'],
|
||||||
|
) ??
|
||||||
|
PostEmbedViewRenderer.webView,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnPostEmbedViewToJson(_SnPostEmbedView instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'uri': instance.uri,
|
||||||
|
'aspect_ratio': instance.aspectRatio,
|
||||||
|
'renderer': _$PostEmbedViewRendererEnumMap[instance.renderer]!,
|
||||||
|
};
|
||||||
|
|
||||||
|
const _$PostEmbedViewRendererEnumMap = {PostEmbedViewRenderer.webView: 0};
|
||||||
|
|||||||
599
lib/pods/activity_rpc.dart
Normal file
599
lib/pods/activity_rpc.dart
Normal file
@@ -0,0 +1,599 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:developer' as developer;
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:shelf/shelf.dart';
|
||||||
|
import 'package:shelf/shelf_io.dart' as shelf_io;
|
||||||
|
import 'package:shelf_web_socket/shelf_web_socket.dart';
|
||||||
|
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
const String kRpcLogPrefix = 'arRPC.websocket';
|
||||||
|
const String kRpcIpcLogPrefix = 'arRPC.ipc';
|
||||||
|
|
||||||
|
// IPC Constants
|
||||||
|
const String kIpcBasePath = 'discord-ipc';
|
||||||
|
|
||||||
|
// IPC Packet Types
|
||||||
|
class IpcTypes {
|
||||||
|
static const int handshake = 0;
|
||||||
|
static const int frame = 1;
|
||||||
|
static const int close = 2;
|
||||||
|
static const int ping = 3;
|
||||||
|
static const int pong = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPC Close Codes
|
||||||
|
class IpcCloseCodes {
|
||||||
|
static const int closeNormal = 1000;
|
||||||
|
static const int closeUnsupported = 1003;
|
||||||
|
static const int closeAbnormal = 1006;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPC Error Codes
|
||||||
|
class IpcErrorCodes {
|
||||||
|
static const int invalidClientId = 4000;
|
||||||
|
static const int invalidOrigin = 4001;
|
||||||
|
static const int rateLimited = 4002;
|
||||||
|
static const int tokenRevoked = 4003;
|
||||||
|
static const int invalidVersion = 4004;
|
||||||
|
static const int invalidEncoding = 4005;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ActivityRpcServer {
|
||||||
|
static const List<int> portRange = [6463, 6472]; // Ports 6463–6472
|
||||||
|
Map<String, Function>
|
||||||
|
handlers; // {connection: (socket), message: (socket, data), close: (socket)}
|
||||||
|
HttpServer? _httpServer;
|
||||||
|
ServerSocket? _ipcServer;
|
||||||
|
final List<WebSocketChannel> _wsSockets = [];
|
||||||
|
final List<_IpcSocketWrapper> _ipcSockets = [];
|
||||||
|
|
||||||
|
ActivityRpcServer(this.handlers);
|
||||||
|
|
||||||
|
void updateHandlers(Map<String, Function> newHandlers) {
|
||||||
|
handlers = newHandlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode IPC packet
|
||||||
|
static Uint8List encodeIpcPacket(int type, Map<String, dynamic> data) {
|
||||||
|
final jsonData = jsonEncode(data);
|
||||||
|
final dataBytes = utf8.encode(jsonData);
|
||||||
|
final dataSize = dataBytes.length;
|
||||||
|
|
||||||
|
final buffer = ByteData(8 + dataSize);
|
||||||
|
buffer.setInt32(0, type, Endian.little);
|
||||||
|
buffer.setInt32(4, dataSize, Endian.little);
|
||||||
|
buffer.buffer.asUint8List().setRange(8, 8 + dataSize, dataBytes);
|
||||||
|
|
||||||
|
return buffer.buffer.asUint8List();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> _getMacOsSystemTmpDir() async {
|
||||||
|
final result = await Process.run('getconf', ['DARWIN_USER_TEMP_DIR']);
|
||||||
|
return (result.stdout as String).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find available IPC socket path
|
||||||
|
Future<String> _findAvailableIpcPath() async {
|
||||||
|
// Build list of directories to try, with macOS-specific handling
|
||||||
|
final baseDirs = <String>[];
|
||||||
|
|
||||||
|
if (Platform.isMacOS) {
|
||||||
|
try {
|
||||||
|
final macTempDir = await _getMacOsSystemTmpDir();
|
||||||
|
if (macTempDir.isNotEmpty) {
|
||||||
|
baseDirs.add(macTempDir);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
developer.log(
|
||||||
|
'Failed to get macOS system temp dir: $e',
|
||||||
|
name: kRpcIpcLogPrefix,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add other standard directories
|
||||||
|
final otherDirs = [
|
||||||
|
Platform.environment['XDG_RUNTIME_DIR'], // User runtime directory
|
||||||
|
Platform.environment['TMPDIR'], // App container temp (fallback)
|
||||||
|
Platform.environment['TMP'],
|
||||||
|
Platform.environment['TEMP'],
|
||||||
|
'/tmp', // System temp directory - most compatible
|
||||||
|
];
|
||||||
|
|
||||||
|
baseDirs.addAll(
|
||||||
|
otherDirs.where((dir) => dir != null && dir.isNotEmpty).cast<String>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (final baseDir in baseDirs) {
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
final socketPath = path.join(baseDir, '$kIpcBasePath-$i');
|
||||||
|
try {
|
||||||
|
final socket = await ServerSocket.bind(
|
||||||
|
InternetAddress(socketPath, type: InternetAddressType.unix),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
socket.close();
|
||||||
|
// Clean up the test socket
|
||||||
|
try {
|
||||||
|
await File(socketPath).delete();
|
||||||
|
} catch (_) {}
|
||||||
|
developer.log(
|
||||||
|
'IPC socket will be created at: $socketPath',
|
||||||
|
name: kRpcIpcLogPrefix,
|
||||||
|
);
|
||||||
|
return socketPath;
|
||||||
|
} catch (e) {
|
||||||
|
// Path not available, try next
|
||||||
|
if (i == 0) {
|
||||||
|
// Log only for the first attempt per directory
|
||||||
|
developer.log(
|
||||||
|
'IPC path $socketPath not available: $e',
|
||||||
|
name: kRpcIpcLogPrefix,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw Exception(
|
||||||
|
'No available IPC socket paths found in any temp directory',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the WebSocket server
|
||||||
|
Future<void> start() async {
|
||||||
|
int port = portRange[0];
|
||||||
|
bool wsSuccess = false;
|
||||||
|
|
||||||
|
// Start WebSocket server
|
||||||
|
while (port <= portRange[1]) {
|
||||||
|
developer.log('trying port $port', name: kRpcLogPrefix);
|
||||||
|
try {
|
||||||
|
// Start HTTP server
|
||||||
|
_httpServer = await HttpServer.bind(InternetAddress.loopbackIPv4, port);
|
||||||
|
developer.log('listening on $port', name: kRpcLogPrefix);
|
||||||
|
|
||||||
|
// Handle WebSocket upgrades
|
||||||
|
shelf_io.serveRequests(_httpServer!, (Request request) async {
|
||||||
|
developer.log('new request', name: kRpcLogPrefix);
|
||||||
|
if (request.headers['upgrade']?.toLowerCase() == 'websocket') {
|
||||||
|
final handler = webSocketHandler((WebSocketChannel channel) {
|
||||||
|
_wsSockets.add(channel);
|
||||||
|
_onWsConnection(channel, request);
|
||||||
|
});
|
||||||
|
return handler(request);
|
||||||
|
}
|
||||||
|
developer.log(
|
||||||
|
'new request disposed due to not websocket',
|
||||||
|
name: kRpcLogPrefix,
|
||||||
|
);
|
||||||
|
return Response.notFound('Not a WebSocket request');
|
||||||
|
});
|
||||||
|
wsSuccess = true;
|
||||||
|
break;
|
||||||
|
} catch (e) {
|
||||||
|
if (e is SocketException && e.osError?.errorCode == 98) {
|
||||||
|
// EADDRINUSE
|
||||||
|
developer.log('$port in use!', name: kRpcLogPrefix);
|
||||||
|
} else {
|
||||||
|
developer.log('http error: $e', name: kRpcLogPrefix);
|
||||||
|
}
|
||||||
|
port++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wsSuccess) {
|
||||||
|
throw Exception(
|
||||||
|
'Failed to bind to any port in range ${portRange[0]}–${portRange[1]}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start IPC server (skip on macOS due to sandboxing)
|
||||||
|
final shouldStartIpc = !Platform.isMacOS;
|
||||||
|
if (shouldStartIpc) {
|
||||||
|
try {
|
||||||
|
final ipcPath = await _findAvailableIpcPath();
|
||||||
|
_ipcServer = await ServerSocket.bind(
|
||||||
|
InternetAddress(ipcPath, type: InternetAddressType.unix),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
developer.log('IPC listening at $ipcPath', name: kRpcIpcLogPrefix);
|
||||||
|
|
||||||
|
_ipcServer!.listen((Socket socket) {
|
||||||
|
_onIpcConnection(socket);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
developer.log('IPC server error: $e', name: kRpcIpcLogPrefix);
|
||||||
|
// Continue without IPC if it fails
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
developer.log(
|
||||||
|
'IPC server disabled on macOS in production mode due to sandboxing',
|
||||||
|
name: kRpcIpcLogPrefix,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the server
|
||||||
|
Future<void> stop() async {
|
||||||
|
// Stop WebSocket server
|
||||||
|
for (var socket in _wsSockets) {
|
||||||
|
await socket.sink.close();
|
||||||
|
}
|
||||||
|
_wsSockets.clear();
|
||||||
|
await _httpServer?.close();
|
||||||
|
|
||||||
|
// Stop IPC server
|
||||||
|
for (var socket in _ipcSockets) {
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
_ipcSockets.clear();
|
||||||
|
await _ipcServer?.close();
|
||||||
|
|
||||||
|
developer.log('servers stopped', name: kRpcLogPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle new WebSocket connection
|
||||||
|
void _onWsConnection(WebSocketChannel socket, Request request) {
|
||||||
|
// Parse query parameters
|
||||||
|
final uri = request.url;
|
||||||
|
final params = uri.queryParameters;
|
||||||
|
final ver = int.tryParse(params['v'] ?? '1') ?? 1;
|
||||||
|
final encoding = params['encoding'] ?? 'json';
|
||||||
|
final clientId = params['client_id'] ?? '';
|
||||||
|
final origin = request.headers['origin'] ?? '';
|
||||||
|
|
||||||
|
developer.log(
|
||||||
|
'new WS connection! origin: $origin, params: $params',
|
||||||
|
name: kRpcLogPrefix,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Validate origin
|
||||||
|
if (origin.isNotEmpty &&
|
||||||
|
![
|
||||||
|
'https://discord.com',
|
||||||
|
'https://ptb.discord.com',
|
||||||
|
'https://canary.discord.com',
|
||||||
|
].contains(origin)) {
|
||||||
|
developer.log('disallowed origin: $origin', name: kRpcLogPrefix);
|
||||||
|
socket.sink.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate encoding
|
||||||
|
if (encoding != 'json') {
|
||||||
|
developer.log(
|
||||||
|
'unsupported encoding requested: $encoding',
|
||||||
|
name: kRpcLogPrefix,
|
||||||
|
);
|
||||||
|
socket.sink.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate version
|
||||||
|
if (ver != 1) {
|
||||||
|
developer.log('unsupported version requested: $ver', name: kRpcLogPrefix);
|
||||||
|
socket.sink.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store client info on socket
|
||||||
|
final socketWithMeta = _WsSocketWrapper(socket, clientId, encoding);
|
||||||
|
|
||||||
|
// Set up event listeners
|
||||||
|
socket.stream.listen(
|
||||||
|
(data) => _onWsMessage(socketWithMeta, data),
|
||||||
|
onError: (e) {
|
||||||
|
developer.log('WS socket error: $e', name: kRpcLogPrefix);
|
||||||
|
},
|
||||||
|
onDone: () {
|
||||||
|
developer.log('WS socket closed', name: kRpcLogPrefix);
|
||||||
|
handlers['close']?.call(socketWithMeta);
|
||||||
|
_wsSockets.remove(socket);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Notify handler of new connection
|
||||||
|
handlers['connection']?.call(socketWithMeta);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle new IPC connection
|
||||||
|
void _onIpcConnection(Socket socket) {
|
||||||
|
developer.log('new IPC connection!', name: kRpcIpcLogPrefix);
|
||||||
|
|
||||||
|
final socketWrapper = _IpcSocketWrapper(socket);
|
||||||
|
_ipcSockets.add(socketWrapper);
|
||||||
|
|
||||||
|
// Set up event listeners
|
||||||
|
socket.listen(
|
||||||
|
(data) => _onIpcData(socketWrapper, data),
|
||||||
|
onError: (e) {
|
||||||
|
developer.log('IPC socket error: $e', name: kRpcIpcLogPrefix);
|
||||||
|
socket.close();
|
||||||
|
},
|
||||||
|
onDone: () {
|
||||||
|
developer.log('IPC socket closed', name: kRpcIpcLogPrefix);
|
||||||
|
handlers['close']?.call(socketWrapper);
|
||||||
|
_ipcSockets.remove(socketWrapper);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle incoming WebSocket message
|
||||||
|
void _onWsMessage(_WsSocketWrapper socket, dynamic data) {
|
||||||
|
try {
|
||||||
|
final jsonData = jsonDecode(data as String);
|
||||||
|
developer.log('WS message: $jsonData', name: kRpcLogPrefix);
|
||||||
|
handlers['message']?.call(socket, jsonData);
|
||||||
|
} catch (e) {
|
||||||
|
developer.log('WS message parse error: $e', name: kRpcLogPrefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle incoming IPC data
|
||||||
|
void _onIpcData(_IpcSocketWrapper socket, List<int> data) {
|
||||||
|
try {
|
||||||
|
socket.addData(data);
|
||||||
|
final packets = socket.readPackets();
|
||||||
|
for (final packet in packets) {
|
||||||
|
_handleIpcPacket(socket, packet);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
developer.log('IPC data error: $e', name: kRpcIpcLogPrefix);
|
||||||
|
socket.closeWithCode(IpcCloseCodes.closeUnsupported, e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle IPC packet
|
||||||
|
void _handleIpcPacket(_IpcSocketWrapper socket, _IpcPacket packet) {
|
||||||
|
switch (packet.type) {
|
||||||
|
case IpcTypes.ping:
|
||||||
|
developer.log('IPC ping received', name: kRpcIpcLogPrefix);
|
||||||
|
socket.sendPong(packet.data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IpcTypes.pong:
|
||||||
|
developer.log('IPC pong received', name: kRpcIpcLogPrefix);
|
||||||
|
// Handle pong if needed
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IpcTypes.handshake:
|
||||||
|
if (socket.handshook) {
|
||||||
|
throw Exception('Already handshook');
|
||||||
|
}
|
||||||
|
socket.handshook = true;
|
||||||
|
_onIpcHandshake(socket, packet.data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IpcTypes.frame:
|
||||||
|
if (!socket.handshook) {
|
||||||
|
throw Exception('Need to handshake first');
|
||||||
|
}
|
||||||
|
developer.log('IPC frame: ${packet.data}', name: kRpcIpcLogPrefix);
|
||||||
|
handlers['message']?.call(socket, packet.data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IpcTypes.close:
|
||||||
|
socket.close();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw Exception('Invalid packet type: ${packet.type}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle IPC handshake
|
||||||
|
void _onIpcHandshake(_IpcSocketWrapper socket, Map<String, dynamic> params) {
|
||||||
|
developer.log('IPC handshake: $params', name: kRpcIpcLogPrefix);
|
||||||
|
|
||||||
|
final ver = int.tryParse(params['v']?.toString() ?? '1') ?? 1;
|
||||||
|
final clientId = params['client_id']?.toString() ?? '';
|
||||||
|
|
||||||
|
// Validate version
|
||||||
|
if (ver != 1) {
|
||||||
|
developer.log(
|
||||||
|
'IPC unsupported version requested: $ver',
|
||||||
|
name: kRpcIpcLogPrefix,
|
||||||
|
);
|
||||||
|
socket.closeWithCode(IpcErrorCodes.invalidVersion);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate client ID
|
||||||
|
if (clientId.isEmpty) {
|
||||||
|
developer.log('IPC client ID required', name: kRpcIpcLogPrefix);
|
||||||
|
socket.closeWithCode(IpcErrorCodes.invalidClientId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.clientId = clientId;
|
||||||
|
|
||||||
|
// Notify handler of new connection
|
||||||
|
handlers['connection']?.call(socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebSocket wrapper
|
||||||
|
class _WsSocketWrapper {
|
||||||
|
final WebSocketChannel channel;
|
||||||
|
final String clientId;
|
||||||
|
final String encoding;
|
||||||
|
|
||||||
|
_WsSocketWrapper(this.channel, this.clientId, this.encoding);
|
||||||
|
|
||||||
|
void send(Map<String, dynamic> msg) {
|
||||||
|
developer.log('WS sending: $msg', name: kRpcLogPrefix);
|
||||||
|
channel.sink.add(jsonEncode(msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPC wrapper
|
||||||
|
class _IpcSocketWrapper {
|
||||||
|
final Socket socket;
|
||||||
|
String clientId = '';
|
||||||
|
bool handshook = false;
|
||||||
|
final List<int> _buffer = [];
|
||||||
|
|
||||||
|
_IpcSocketWrapper(this.socket);
|
||||||
|
|
||||||
|
void addData(List<int> data) {
|
||||||
|
_buffer.addAll(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send(Map<String, dynamic> msg) {
|
||||||
|
developer.log('IPC sending: $msg', name: kRpcIpcLogPrefix);
|
||||||
|
final packet = ActivityRpcServer.encodeIpcPacket(IpcTypes.frame, msg);
|
||||||
|
socket.add(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendPong(dynamic data) {
|
||||||
|
final packet = ActivityRpcServer.encodeIpcPacket(IpcTypes.pong, data ?? {});
|
||||||
|
socket.add(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() {
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void closeWithCode(int code, [String message = '']) {
|
||||||
|
final closeData = {'code': code, 'message': message};
|
||||||
|
final packet = ActivityRpcServer.encodeIpcPacket(IpcTypes.close, closeData);
|
||||||
|
socket.add(packet);
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<_IpcPacket> readPackets() {
|
||||||
|
final packets = <_IpcPacket>[];
|
||||||
|
|
||||||
|
while (_buffer.length >= 8) {
|
||||||
|
final buffer = Uint8List.fromList(_buffer);
|
||||||
|
final byteData = ByteData.view(buffer.buffer);
|
||||||
|
|
||||||
|
final type = byteData.getInt32(0, Endian.little);
|
||||||
|
final dataSize = byteData.getInt32(4, Endian.little);
|
||||||
|
|
||||||
|
if (_buffer.length < 8 + dataSize) break;
|
||||||
|
|
||||||
|
final dataBytes = _buffer.sublist(8, 8 + dataSize);
|
||||||
|
final jsonStr = utf8.decode(dataBytes);
|
||||||
|
final jsonData = jsonDecode(jsonStr);
|
||||||
|
|
||||||
|
packets.add(_IpcPacket(type, jsonData));
|
||||||
|
|
||||||
|
_buffer.removeRange(0, 8 + dataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return packets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPC Packet structure
|
||||||
|
class _IpcPacket {
|
||||||
|
final int type;
|
||||||
|
final Map<String, dynamic> data;
|
||||||
|
|
||||||
|
_IpcPacket(this.type, this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// State management for server status and activities
|
||||||
|
class ServerState {
|
||||||
|
final String status;
|
||||||
|
final List<String> activities;
|
||||||
|
|
||||||
|
ServerState({required this.status, this.activities = const []});
|
||||||
|
|
||||||
|
ServerState copyWith({String? status, List<String>? activities}) {
|
||||||
|
return ServerState(
|
||||||
|
status: status ?? this.status,
|
||||||
|
activities: activities ?? this.activities,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServerStateNotifier extends StateNotifier<ServerState> {
|
||||||
|
final ActivityRpcServer server;
|
||||||
|
|
||||||
|
ServerStateNotifier(this.server)
|
||||||
|
: super(ServerState(status: 'Server not started'));
|
||||||
|
|
||||||
|
Future<void> start() async {
|
||||||
|
try {
|
||||||
|
await server.start();
|
||||||
|
state = state.copyWith(status: 'Server running');
|
||||||
|
} catch (e) {
|
||||||
|
state = state.copyWith(status: 'Server failed: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateStatus(String status) {
|
||||||
|
state = state.copyWith(status: status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addActivity(String activity) {
|
||||||
|
state = state.copyWith(activities: [...state.activities, activity]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Providers
|
||||||
|
final rpcServerStateProvider =
|
||||||
|
StateNotifierProvider<ServerStateNotifier, ServerState>((ref) {
|
||||||
|
final server = ActivityRpcServer({});
|
||||||
|
final notifier = ServerStateNotifier(server);
|
||||||
|
server.updateHandlers({
|
||||||
|
'connection': (socket) {
|
||||||
|
final clientId =
|
||||||
|
socket is _WsSocketWrapper
|
||||||
|
? socket.clientId
|
||||||
|
: (socket as _IpcSocketWrapper).clientId;
|
||||||
|
notifier.updateStatus('Client connected (ID: $clientId)');
|
||||||
|
// Send READY event
|
||||||
|
socket.send({
|
||||||
|
'cmd': 'DISPATCH',
|
||||||
|
'data': {
|
||||||
|
'v': 1,
|
||||||
|
'config': {
|
||||||
|
'cdn_host': 'fake.cdn',
|
||||||
|
'api_endpoint': '//fake.api',
|
||||||
|
'environment': 'dev',
|
||||||
|
},
|
||||||
|
'user': {
|
||||||
|
'id': 'fake_user_id',
|
||||||
|
'username': 'FakeUser',
|
||||||
|
'discriminator': '0001',
|
||||||
|
'avatar': null,
|
||||||
|
'bot': false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'evt': 'READY',
|
||||||
|
'nonce': '12345', // Should be dynamic
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'message': (socket, dynamic data) {
|
||||||
|
if (data['cmd'] == 'SET_ACTIVITY') {
|
||||||
|
notifier.addActivity(
|
||||||
|
'Activity: ${data['args']['activity']['details'] ?? 'Unknown'}',
|
||||||
|
);
|
||||||
|
// Echo back success
|
||||||
|
socket.send({
|
||||||
|
'cmd': 'SET_ACTIVITY',
|
||||||
|
'data': data['args']['activity'],
|
||||||
|
'evt': null,
|
||||||
|
'nonce': data['nonce'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'close': (socket) {
|
||||||
|
notifier.updateStatus('Client disconnected');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return notifier;
|
||||||
|
});
|
||||||
|
|
||||||
|
final rpcServerProvider = Provider<ActivityRpcServer>((ref) {
|
||||||
|
final notifier = ref.watch(rpcServerStateProvider.notifier);
|
||||||
|
return notifier.server;
|
||||||
|
});
|
||||||
@@ -4,6 +4,7 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/auth.dart';
|
import 'package:island/models/auth.dart';
|
||||||
import 'package:island/models/account.dart';
|
import 'package:island/models/account.dart';
|
||||||
@@ -479,6 +480,7 @@ class AccountSettingsScreen extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
const Gap(8),
|
||||||
]
|
]
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -62,6 +62,32 @@ class ContactMethodSheet extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> makeContactMethodPublic() async {
|
||||||
|
try {
|
||||||
|
showLoadingModal(context);
|
||||||
|
final client = ref.read(apiClientProvider);
|
||||||
|
await client.post('/id/accounts/me/contacts/${contact.id}/public');
|
||||||
|
if (context.mounted) Navigator.pop(context, true);
|
||||||
|
} catch (err) {
|
||||||
|
showErrorAlert(err);
|
||||||
|
} finally {
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> makeContactMethodPrivate() async {
|
||||||
|
try {
|
||||||
|
showLoadingModal(context);
|
||||||
|
final client = ref.read(apiClientProvider);
|
||||||
|
await client.delete('/id/accounts/me/contacts/${contact.id}/public');
|
||||||
|
if (context.mounted) Navigator.pop(context, true);
|
||||||
|
} catch (err) {
|
||||||
|
showErrorAlert(err);
|
||||||
|
} finally {
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return SheetScaffold(
|
return SheetScaffold(
|
||||||
titleText: 'contactMethod'.tr(),
|
titleText: 'contactMethod'.tr(),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -111,6 +137,27 @@ class ContactMethodSheet extends HookConsumerWidget {
|
|||||||
backgroundColor: Theme.of(context).colorScheme.tertiary,
|
backgroundColor: Theme.of(context).colorScheme.tertiary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (contact.isPublic)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
|
child: Badge(
|
||||||
|
label: Text('contactMethodPublic'.tr()),
|
||||||
|
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (!contact.isPublic)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
|
child: Badge(
|
||||||
|
label: Text('contactMethodPrivate'.tr()),
|
||||||
|
textColor: Theme.of(context).colorScheme.onSurface,
|
||||||
|
backgroundColor:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.surfaceContainerHighest,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -130,6 +177,20 @@ class ContactMethodSheet extends HookConsumerWidget {
|
|||||||
onTap: setContactMethodAsPrimary,
|
onTap: setContactMethodAsPrimary,
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 20),
|
contentPadding: EdgeInsets.symmetric(horizontal: 20),
|
||||||
),
|
),
|
||||||
|
if (contact.verifiedAt != null && !contact.isPublic)
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Symbols.public),
|
||||||
|
title: Text('contactMethodMakePublic').tr(),
|
||||||
|
onTap: makeContactMethodPublic,
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
),
|
||||||
|
if (contact.verifiedAt != null && contact.isPublic)
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Symbols.visibility_off),
|
||||||
|
title: Text('contactMethodMakePrivate').tr(),
|
||||||
|
onTap: makeContactMethodPrivate,
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Symbols.delete),
|
leading: const Icon(Symbols.delete),
|
||||||
title: Text('contactMethodDelete').tr(),
|
title: Text('contactMethodDelete').tr(),
|
||||||
|
|||||||
@@ -321,6 +321,59 @@ class _AccountProfileLinks extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _AccountProfileContacts extends StatelessWidget {
|
||||||
|
final SnAccount data;
|
||||||
|
|
||||||
|
const _AccountProfileContacts({required this.data});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final publicContacts = data.contacts.where((c) => c.isPublic).toList();
|
||||||
|
if (publicContacts.isEmpty) return const SizedBox.shrink();
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'contactMethod',
|
||||||
|
).tr().bold().padding(horizontal: 24, top: 12, bottom: 4),
|
||||||
|
for (final contact in publicContacts)
|
||||||
|
ListTile(
|
||||||
|
title: Text(contact.content),
|
||||||
|
subtitle: Text(switch (contact.type) {
|
||||||
|
0 => 'contactMethodTypeEmail'.tr(),
|
||||||
|
1 => 'contactMethodTypePhone'.tr(),
|
||||||
|
_ => 'contactMethodTypeAddress'.tr(),
|
||||||
|
}),
|
||||||
|
leading: Icon(switch (contact.type) {
|
||||||
|
0 => Symbols.mail,
|
||||||
|
1 => Symbols.phone,
|
||||||
|
_ => Symbols.home,
|
||||||
|
}),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
switch (contact.type) {
|
||||||
|
case 0:
|
||||||
|
launchUrlString('mailto:${contact.content}');
|
||||||
|
case 1:
|
||||||
|
launchUrlString('tel:${contact.content}');
|
||||||
|
default:
|
||||||
|
// For address, maybe copy to clipboard or do nothing
|
||||||
|
Clipboard.setData(ClipboardData(text: contact.content));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _AccountPublisherList extends StatelessWidget {
|
class _AccountPublisherList extends StatelessWidget {
|
||||||
final List<SnPublisher> publishers;
|
final List<SnPublisher> publishers;
|
||||||
|
|
||||||
@@ -792,6 +845,10 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: _AccountProfileLinks(data: data),
|
child: _AccountProfileLinks(data: data),
|
||||||
),
|
),
|
||||||
|
if (data.contacts.any((c) => c.isPublic))
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: _AccountProfileContacts(data: data),
|
||||||
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: _AccountProfileDetail(data: data),
|
child: _AccountProfileDetail(data: data),
|
||||||
),
|
),
|
||||||
@@ -916,6 +973,12 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
data: data,
|
data: data,
|
||||||
).padding(horizontal: 4),
|
).padding(horizontal: 4),
|
||||||
),
|
),
|
||||||
|
if (data.contacts.any((c) => c.isPublic))
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: _AccountProfileContacts(
|
||||||
|
data: data,
|
||||||
|
).padding(horizontal: 4),
|
||||||
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: _AccountPublisherList(
|
child: _AccountPublisherList(
|
||||||
publishers: accountPublishers.value ?? [],
|
publishers: accountPublishers.value ?? [],
|
||||||
|
|||||||
@@ -727,7 +727,11 @@ class _ChatMemberListSheet extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
Flexible(child: Text(member.account.nick)),
|
Flexible(child: Text(member.account.nick)),
|
||||||
if (member.status != null)
|
if (member.status != null)
|
||||||
AccountStatusLabel(status: member.status!),
|
AccountStatusLabel(
|
||||||
|
status: member.status!,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
if (member.joinedAt == null)
|
if (member.joinedAt == null)
|
||||||
const Icon(Symbols.pending_actions, size: 20),
|
const Icon(Symbols.pending_actions, size: 20),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -128,14 +128,6 @@ class ArticleComposeScreen extends HookConsumerWidget {
|
|||||||
return null;
|
return null;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Auto-save cleanup
|
|
||||||
useEffect(() {
|
|
||||||
return () {
|
|
||||||
state.stopAutoSave();
|
|
||||||
ComposeLogic.dispose(state);
|
|
||||||
};
|
|
||||||
}, [state]);
|
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
void showSettingsSheet() {
|
void showSettingsSheet() {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
@@ -182,6 +174,12 @@ class ArticleComposeScreen extends HookConsumerWidget {
|
|||||||
MarkdownTextContent(
|
MarkdownTextContent(
|
||||||
content: contentValue.text,
|
content: contentValue.text,
|
||||||
textStyle: theme.textTheme.bodyMedium,
|
textStyle: theme.textTheme.bodyMedium,
|
||||||
|
attachments:
|
||||||
|
state.attachments.value
|
||||||
|
.where((e) => e.isOnCloud)
|
||||||
|
.map((e) => e.data)
|
||||||
|
.cast<SnCloudFile>()
|
||||||
|
.toList(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -268,7 +266,7 @@ class ArticleComposeScreen extends HookConsumerWidget {
|
|||||||
child: KeyboardListener(
|
child: KeyboardListener(
|
||||||
focusNode: FocusNode(),
|
focusNode: FocusNode(),
|
||||||
onKeyEvent:
|
onKeyEvent:
|
||||||
(event) => _handleKeyPress(
|
(event) => ComposeLogic.handleKeyPress(
|
||||||
event,
|
event,
|
||||||
state,
|
state,
|
||||||
ref,
|
ref,
|
||||||
@@ -511,38 +509,4 @@ class ArticleComposeScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method to handle keyboard shortcuts
|
|
||||||
void _handleKeyPress(
|
|
||||||
KeyEvent event,
|
|
||||||
ComposeState state,
|
|
||||||
WidgetRef ref,
|
|
||||||
BuildContext context, {
|
|
||||||
SnPost? originalPost,
|
|
||||||
}) {
|
|
||||||
if (event is! RawKeyDownEvent) return;
|
|
||||||
|
|
||||||
final isPaste = event.logicalKey == LogicalKeyboardKey.keyV;
|
|
||||||
final isSave = event.logicalKey == LogicalKeyboardKey.keyS;
|
|
||||||
final isModifierPressed =
|
|
||||||
HardwareKeyboard.instance.isMetaPressed ||
|
|
||||||
HardwareKeyboard.instance.isControlPressed;
|
|
||||||
final isSubmit = event.logicalKey == LogicalKeyboardKey.enter;
|
|
||||||
|
|
||||||
if (isPaste && isModifierPressed) {
|
|
||||||
ComposeLogic.handlePaste(state);
|
|
||||||
} else if (isSave && isModifierPressed) {
|
|
||||||
ComposeLogic.saveDraft(ref, state);
|
|
||||||
ComposeLogic.saveDraft(ref, state);
|
|
||||||
} else if (isSubmit && isModifierPressed && !state.submitting.value) {
|
|
||||||
ComposeLogic.performAction(
|
|
||||||
ref,
|
|
||||||
state,
|
|
||||||
context,
|
|
||||||
originalPost: originalPost,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper method to save article draft
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -277,8 +277,6 @@ class PostSearchScreen extends HookConsumerWidget {
|
|||||||
Text('pinned'.tr()),
|
Text('pinned'.tr()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
// TODO: Add dropdown for type selection
|
|
||||||
// TODO: Add multi-select for categories and tags
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -668,7 +668,13 @@ class _RealmMemberListSheet extends HookConsumerWidget {
|
|||||||
title: Row(
|
title: Row(
|
||||||
spacing: 6,
|
spacing: 6,
|
||||||
children: [
|
children: [
|
||||||
Flexible(child: Text(member.account!.nick)),
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
member.account!.nick,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
if (member.status != null)
|
if (member.status != null)
|
||||||
AccountStatusLabel(status: member.status!),
|
AccountStatusLabel(status: member.status!),
|
||||||
if (member.joinedAt == null)
|
if (member.joinedAt == null)
|
||||||
|
|||||||
@@ -39,8 +39,13 @@ class ComposeStorageNotifier extends _$ComposeStorageNotifier {
|
|||||||
await database.addPostDraft(
|
await database.addPostDraft(
|
||||||
PostDraftsCompanion(
|
PostDraftsCompanion(
|
||||||
id: Value(updatedDraft.id),
|
id: Value(updatedDraft.id),
|
||||||
post: Value(jsonEncode(updatedDraft.toJson())),
|
title: Value(updatedDraft.title),
|
||||||
|
description: Value(updatedDraft.description),
|
||||||
|
content: Value(updatedDraft.content),
|
||||||
|
visibility: Value(updatedDraft.visibility),
|
||||||
|
type: Value(updatedDraft.type),
|
||||||
lastModified: Value(updatedDraft.updatedAt ?? DateTime.now()),
|
lastModified: Value(updatedDraft.updatedAt ?? DateTime.now()),
|
||||||
|
postData: Value(jsonEncode(updatedDraft.toJson())),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ part of 'compose_storage_db.dart';
|
|||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$composeStorageNotifierHash() =>
|
String _$composeStorageNotifierHash() =>
|
||||||
r'4ab4dce85d0a961f096dc3b11505f8f0964dee9d';
|
r'8baf17aa06b6f69641c20645ba8a3dfe01c97f8c';
|
||||||
|
|
||||||
/// See also [ComposeStorageNotifier].
|
/// See also [ComposeStorageNotifier].
|
||||||
@ProviderFor(ComposeStorageNotifier)
|
@ProviderFor(ComposeStorageNotifier)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:bitsdojo_window/bitsdojo_window.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/pods/activity_rpc.dart';
|
||||||
import 'package:island/pods/websocket.dart';
|
import 'package:island/pods/websocket.dart';
|
||||||
import 'package:island/screens/tray_manager.dart';
|
import 'package:island/screens/tray_manager.dart';
|
||||||
import 'package:island/services/notify.dart';
|
import 'package:island/services/notify.dart';
|
||||||
@@ -31,7 +32,10 @@ class AppWrapper extends HookConsumerWidget with TrayListener {
|
|||||||
|
|
||||||
TrayService.instance.initialize(this);
|
TrayService.instance.initialize(this);
|
||||||
|
|
||||||
|
ref.read(rpcServerStateProvider.notifier).start();
|
||||||
|
|
||||||
return () {
|
return () {
|
||||||
|
ref.read(rpcServerProvider).stop();
|
||||||
TrayService.instance.dispose(this);
|
TrayService.instance.dispose(this);
|
||||||
sharingService.dispose();
|
sharingService.dispose();
|
||||||
ntySubs?.cancel();
|
ntySubs?.cancel();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
@@ -57,11 +58,21 @@ class CheckInWidget extends HookConsumerWidget {
|
|||||||
final todayResult = ref.watch(checkInResultTodayProvider);
|
final todayResult = ref.watch(checkInResultTodayProvider);
|
||||||
final nextNotableDay = ref.watch(nextNotableDayProvider);
|
final nextNotableDay = ref.watch(nextNotableDayProvider);
|
||||||
|
|
||||||
|
// Update time every second for live progress
|
||||||
|
final currentTime = useState(DateTime.now());
|
||||||
|
useEffect(() {
|
||||||
|
final timer = Timer.periodic(const Duration(seconds: 1), (_) {
|
||||||
|
currentTime.value = DateTime.now();
|
||||||
|
});
|
||||||
|
return timer.cancel;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
final now = currentTime.value;
|
||||||
|
|
||||||
final userinfo = ref.watch(userInfoProvider);
|
final userinfo = ref.watch(userInfoProvider);
|
||||||
final isAdult = useMemoized(() {
|
final isAdult = useMemoized(() {
|
||||||
final birthday = userinfo.value?.profile.birthday;
|
final birthday = userinfo.value?.profile.birthday;
|
||||||
if (birthday == null) return false;
|
if (birthday == null) return false;
|
||||||
final now = DateTime.now();
|
|
||||||
final age =
|
final age =
|
||||||
now.year -
|
now.year -
|
||||||
birthday.year -
|
birthday.year -
|
||||||
@@ -72,6 +83,12 @@ class CheckInWidget extends HookConsumerWidget {
|
|||||||
return age >= 18;
|
return age >= 18;
|
||||||
}, [userinfo]);
|
}, [userinfo]);
|
||||||
|
|
||||||
|
final progress = (now.hour * 60.0 + now.minute) / (24 * 60);
|
||||||
|
final endOfDay = DateTime(now.year, now.month, now.day, 23, 59, 59);
|
||||||
|
final timeLeft = endOfDay.difference(now);
|
||||||
|
final timeLeftFormatted =
|
||||||
|
'${timeLeft.inHours.toString().padLeft(2, '0')}:${(timeLeft.inMinutes % 60).toString().padLeft(2, '0')}:${(timeLeft.inSeconds % 60).toString().padLeft(2, '0')}';
|
||||||
|
|
||||||
Future<void> checkIn({String? captchatTk}) async {
|
Future<void> checkIn({String? captchatTk}) async {
|
||||||
final client = ref.read(apiClientProvider);
|
final client = ref.read(apiClientProvider);
|
||||||
try {
|
try {
|
||||||
@@ -100,106 +117,102 @@ class CheckInWidget extends HookConsumerWidget {
|
|||||||
return Card(
|
return Card(
|
||||||
margin:
|
margin:
|
||||||
margin ?? EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8),
|
margin ?? EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8),
|
||||||
child: Column(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
spacing: 8,
|
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Expanded(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Row(
|
children: [
|
||||||
spacing: 8,
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
spacing: 6,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
Icon(
|
children: [
|
||||||
switch (DateTime.now().weekday) {
|
Icon(
|
||||||
6 || 7 => Symbols.weekend,
|
switch (DateTime.now().weekday) {
|
||||||
_ => isAdult ? Symbols.work : Symbols.school,
|
6 || 7 => Symbols.weekend,
|
||||||
},
|
_ => isAdult ? Symbols.work : Symbols.school,
|
||||||
fill: 1,
|
},
|
||||||
size: 16,
|
fill: 1,
|
||||||
).padding(right: 2),
|
size: 16,
|
||||||
Text(DateFormat('EEE').format(DateTime.now()))
|
).padding(right: 2),
|
||||||
.fontSize(16)
|
Text(
|
||||||
.bold()
|
DateFormat('EEE').format(DateTime.now()),
|
||||||
.textColor(
|
).fontSize(16).bold(),
|
||||||
Theme.of(context).colorScheme.onSecondaryContainer,
|
Text(
|
||||||
|
DateFormat('MM/dd').format(DateTime.now()),
|
||||||
|
).fontSize(16),
|
||||||
|
Tooltip(
|
||||||
|
message: timeLeftFormatted,
|
||||||
|
child: SizedBox(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
value: progress,
|
||||||
|
strokeWidth: 2,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Text(DateFormat('MM/dd').format(DateTime.now()))
|
|
||||||
.fontSize(12)
|
|
||||||
.textColor(
|
|
||||||
Theme.of(context).colorScheme.onSecondaryContainer,
|
|
||||||
)
|
|
||||||
.padding(top: 2),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
spacing: 5,
|
|
||||||
children: [
|
|
||||||
Text('notableDayNext')
|
|
||||||
.tr(args: [nextNotableDay.value?.localName ?? 'idk'])
|
|
||||||
.fontSize(12),
|
|
||||||
SlideCountdown(
|
|
||||||
decoration: const BoxDecoration(),
|
|
||||||
style: const TextStyle(fontSize: 12),
|
|
||||||
separatorStyle: const TextStyle(fontSize: 12),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
duration: nextNotableDay.value?.date.difference(
|
|
||||||
DateTime.now(),
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
Row(
|
||||||
],
|
spacing: 5,
|
||||||
).padding(horizontal: 16, top: 8),
|
children: [
|
||||||
const Divider(height: 1),
|
Text('notableDayNext')
|
||||||
Row(
|
.tr(args: [nextNotableDay.value?.localName ?? 'idk'])
|
||||||
children: [
|
.fontSize(12),
|
||||||
Expanded(
|
if (nextNotableDay.value != null)
|
||||||
child: AnimatedSwitcher(
|
SlideCountdown(
|
||||||
|
decoration: const BoxDecoration(),
|
||||||
|
style: const TextStyle(fontSize: 12),
|
||||||
|
separatorStyle: const TextStyle(fontSize: 12),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
duration: nextNotableDay.value?.date.difference(
|
||||||
|
DateTime.now(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Gap(2),
|
||||||
|
AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
child: todayResult.when(
|
child: todayResult.when(
|
||||||
data: (result) {
|
data: (result) {
|
||||||
if (result == null) return _CheckInNoneWidget();
|
if (result == null) {
|
||||||
return Column(
|
return Text('checkInNoneHint').tr().fontSize(11);
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
}
|
||||||
children: [
|
return Wrap(
|
||||||
Text(
|
alignment: WrapAlignment.start,
|
||||||
'checkInResultLevel${result.level}',
|
runAlignment: WrapAlignment.start,
|
||||||
).tr().fontSize(15).bold(),
|
children:
|
||||||
Wrap(
|
result.tips
|
||||||
children:
|
.map((e) {
|
||||||
result.tips
|
return Row(
|
||||||
.map((e) {
|
mainAxisSize: MainAxisSize.min,
|
||||||
return Row(
|
children: [
|
||||||
mainAxisSize: MainAxisSize.min,
|
Icon(
|
||||||
children: [
|
e.isPositive
|
||||||
Icon(
|
? Symbols.thumb_up
|
||||||
e.isPositive
|
: Symbols.thumb_down,
|
||||||
? Symbols.thumb_up
|
size: 12,
|
||||||
: Symbols.thumb_down,
|
),
|
||||||
size: 12,
|
const Gap(4),
|
||||||
),
|
Text(e.title).fontSize(11),
|
||||||
const Gap(4),
|
],
|
||||||
Text(e.title).fontSize(11),
|
);
|
||||||
],
|
})
|
||||||
);
|
.toList()
|
||||||
})
|
.expand(
|
||||||
.toList()
|
(widget) => [
|
||||||
.expand(
|
widget,
|
||||||
(widget) => [
|
Text(' · ').fontSize(11),
|
||||||
widget,
|
],
|
||||||
Text(' · ').fontSize(11),
|
)
|
||||||
],
|
.toList()
|
||||||
)
|
..removeLast(),
|
||||||
.toList()
|
|
||||||
..removeLast(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
loading: () => _CheckInNoneWidget(),
|
loading: () => Text('checkInNoneHint').tr().fontSize(11),
|
||||||
error:
|
error:
|
||||||
(err, stack) => Column(
|
(err, stack) => Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -209,9 +222,35 @@ class CheckInWidget extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
).alignment(Alignment.centerLeft),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
spacing: 3,
|
||||||
|
children: [
|
||||||
|
AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
child: todayResult.when(
|
||||||
|
data: (result) {
|
||||||
|
return Text(
|
||||||
|
result == null
|
||||||
|
? 'checkInNone'
|
||||||
|
: 'checkInResultLevel${result.level}',
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
).tr().fontSize(15).bold();
|
||||||
|
},
|
||||||
|
loading: () => Text('checkInNone').tr().fontSize(15).bold(),
|
||||||
|
error: (err, stack) => Text('error').tr().fontSize(15).bold(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton.outlined(
|
IconButton.outlined(
|
||||||
|
iconSize: 16,
|
||||||
|
visualDensity: const VisualDensity(
|
||||||
|
horizontal: -3,
|
||||||
|
vertical: -2,
|
||||||
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (todayResult.valueOrNull == null) {
|
if (todayResult.valueOrNull == null) {
|
||||||
checkIn();
|
checkIn();
|
||||||
@@ -238,24 +277,9 @@ class CheckInWidget extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16, bottom: 12, top: 4),
|
),
|
||||||
],
|
],
|
||||||
),
|
).padding(horizontal: 16, vertical: 12),
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CheckInNoneWidget extends StatelessWidget {
|
|
||||||
const _CheckInNoneWidget();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
Text('checkInNone').tr().fontSize(15).bold(),
|
|
||||||
Text('checkInNoneHint').tr().fontSize(11),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,17 +6,21 @@ import 'package:flutter_platform_alert/flutter_platform_alert.dart';
|
|||||||
|
|
||||||
String _parseRemoteError(DioException err) {
|
String _parseRemoteError(DioException err) {
|
||||||
log('${err.requestOptions.method} ${err.requestOptions.uri} ${err.message}');
|
log('${err.requestOptions.method} ${err.requestOptions.uri} ${err.message}');
|
||||||
if (err.response?.data is String) return err.response?.data;
|
String? message;
|
||||||
if (err.response?.data?['errors'] != null) {
|
if (err.response?.data is String) {
|
||||||
|
message = err.response?.data;
|
||||||
|
} else if (err.response?.data?['errors'] != null) {
|
||||||
final errors = err.response?.data['errors'] as Map<String, dynamic>;
|
final errors = err.response?.data['errors'] as Map<String, dynamic>;
|
||||||
return errors.values
|
message = errors.values
|
||||||
.map(
|
.map(
|
||||||
(ele) =>
|
(ele) =>
|
||||||
(ele as List<dynamic>).map((ele) => ele.toString()).join('\n'),
|
(ele as List<dynamic>).map((ele) => ele.toString()).join('\n'),
|
||||||
)
|
)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
}
|
}
|
||||||
return err.message ?? err.toString();
|
if (message == null || message.isEmpty) message = err.response?.statusMessage;
|
||||||
|
message ??= err.message;
|
||||||
|
return message ?? err.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void showErrorAlert(dynamic err) async {
|
void showErrorAlert(dynamic err) async {
|
||||||
|
|||||||
@@ -830,7 +830,7 @@ class _CloudFileListEntry extends HookConsumerWidget {
|
|||||||
bg = ImageFiltered(
|
bg = ImageFiltered(
|
||||||
imageFilter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
|
imageFilter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
|
||||||
child: CloudFileWidget(
|
child: CloudFileWidget(
|
||||||
fit: fit,
|
fit: BoxFit.cover,
|
||||||
item: file,
|
item: file,
|
||||||
noBlurhash: true,
|
noBlurhash: true,
|
||||||
useInternalGate: false,
|
useInternalGate: false,
|
||||||
|
|||||||
@@ -270,6 +270,8 @@ class _PaymentContentState extends ConsumerState<_PaymentContent> {
|
|||||||
}
|
}
|
||||||
} else if (err.response?.statusCode == 400) {
|
} else if (err.response?.statusCode == 400) {
|
||||||
errorMessage = err.response?.data?['error'] ?? errorMessage;
|
errorMessage = err.response?.data?['error'] ?? errorMessage;
|
||||||
|
} else {
|
||||||
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw errorMessage;
|
throw errorMessage;
|
||||||
@@ -419,42 +421,48 @@ class _PaymentContentState extends ConsumerState<_PaymentContent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBiometricAuth() {
|
Widget _buildBiometricAuth() {
|
||||||
return Column(
|
return SingleChildScrollView(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child:
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
Column(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
Icon(Symbols.fingerprint, size: 48),
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
const Gap(16),
|
children: [
|
||||||
Text(
|
Icon(Symbols.fingerprint, size: 48),
|
||||||
'useBiometricToConfirm'.tr(),
|
const Gap(16),
|
||||||
style: Theme.of(
|
Text(
|
||||||
context,
|
'useBiometricToConfirm'.tr(),
|
||||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500),
|
style: Theme.of(
|
||||||
textAlign: TextAlign.center,
|
context,
|
||||||
),
|
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500),
|
||||||
Text(
|
textAlign: TextAlign.center,
|
||||||
'The biometric data will only be processed on your device',
|
),
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
Text(
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
'The biometric data will only be processed on your device',
|
||||||
fontSize: 11,
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
),
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
textAlign: TextAlign.center,
|
fontSize: 11,
|
||||||
).opacity(0.75),
|
),
|
||||||
const Gap(28),
|
textAlign: TextAlign.center,
|
||||||
ElevatedButton.icon(
|
).opacity(0.75),
|
||||||
onPressed: _authenticateWithBiometric,
|
const Gap(28),
|
||||||
icon: const Icon(Symbols.fingerprint),
|
ElevatedButton.icon(
|
||||||
label: Text('authenticateNow'.tr()),
|
onPressed: _authenticateWithBiometric,
|
||||||
style: ElevatedButton.styleFrom(
|
icon: const Icon(Symbols.fingerprint),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
label: Text('authenticateNow'.tr()),
|
||||||
),
|
style: ElevatedButton.styleFrom(
|
||||||
),
|
padding: const EdgeInsets.symmetric(
|
||||||
TextButton(
|
horizontal: 24,
|
||||||
onPressed: () => _fallbackToPinMode(null),
|
vertical: 12,
|
||||||
child: Text('usePinInstead'.tr()),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
).center();
|
TextButton(
|
||||||
|
onPressed: () => _fallbackToPinMode(null),
|
||||||
|
child: Text('usePinInstead'.tr()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).center(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildActionButtons() {
|
Widget _buildActionButtons() {
|
||||||
|
|||||||
@@ -44,13 +44,13 @@ class PollStatsWidget extends StatelessWidget {
|
|||||||
// yes/no: map {true: count, false: count}
|
// yes/no: map {true: count, false: count}
|
||||||
if (raw is Map) {
|
if (raw is Map) {
|
||||||
final int yes =
|
final int yes =
|
||||||
(raw[true] is int)
|
(raw['true'] is int)
|
||||||
? raw[true] as int
|
? raw['true'] as int
|
||||||
: int.tryParse('${raw[true]}') ?? 0;
|
: int.tryParse('${raw['true']}') ?? 0;
|
||||||
final int no =
|
final int no =
|
||||||
(raw[false] is int)
|
(raw['false'] is int)
|
||||||
? raw[false] as int
|
? raw['false'] as int
|
||||||
: int.tryParse('${raw[false]}') ?? 0;
|
: int.tryParse('${raw['false']}') ?? 0;
|
||||||
final total = (yes + no).clamp(0, 1 << 31);
|
final total = (yes + no).clamp(0, 1 << 31);
|
||||||
final yesPct = total == 0 ? 0.0 : yes / total;
|
final yesPct = total == 0 ? 0.0 : yes / total;
|
||||||
final noPct = total == 0 ? 0.0 : no / total;
|
final noPct = total == 0 ? 0.0 : no / total;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class PollSubmit extends ConsumerStatefulWidget {
|
|||||||
this.onCancel,
|
this.onCancel,
|
||||||
this.showProgress = true,
|
this.showProgress = true,
|
||||||
this.isReadonly = false,
|
this.isReadonly = false,
|
||||||
|
this.isInitiallyExpanded = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
final SnPollWithStats poll;
|
final SnPollWithStats poll;
|
||||||
@@ -36,6 +37,9 @@ class PollSubmit extends ConsumerStatefulWidget {
|
|||||||
|
|
||||||
final bool isReadonly;
|
final bool isReadonly;
|
||||||
|
|
||||||
|
/// Whether the poll should start expanded instead of collapsed.
|
||||||
|
final bool isInitiallyExpanded;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<PollSubmit> createState() => _PollSubmitState();
|
ConsumerState<PollSubmit> createState() => _PollSubmitState();
|
||||||
}
|
}
|
||||||
@@ -45,6 +49,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
int _index = 0;
|
int _index = 0;
|
||||||
bool _submitting = false;
|
bool _submitting = false;
|
||||||
bool _isModifying = false; // New state to track if user is modifying answers
|
bool _isModifying = false; // New state to track if user is modifying answers
|
||||||
|
bool _isCollapsed = true; // New state to track collapse/expand
|
||||||
|
|
||||||
/// Collected answers, keyed by questionId
|
/// Collected answers, keyed by questionId
|
||||||
late Map<String, dynamic> _answers;
|
late Map<String, dynamic> _answers;
|
||||||
@@ -65,6 +70,8 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
_questions = [...widget.poll.questions]
|
_questions = [...widget.poll.questions]
|
||||||
..sort((a, b) => a.order.compareTo(b.order));
|
..sort((a, b) => a.order.compareTo(b.order));
|
||||||
_answers = Map<String, dynamic>.from(widget.initialAnswers ?? {});
|
_answers = Map<String, dynamic>.from(widget.initialAnswers ?? {});
|
||||||
|
// Set initial collapse state based on the parameter
|
||||||
|
_isCollapsed = !widget.isInitiallyExpanded;
|
||||||
if (!widget.isReadonly) {
|
if (!widget.isReadonly) {
|
||||||
_loadCurrentIntoLocalState();
|
_loadCurrentIntoLocalState();
|
||||||
// If initial answers are provided, set _isModifying to false initially
|
// If initial answers are provided, set _isModifying to false initially
|
||||||
@@ -405,6 +412,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
multiSelectionEnabled: false,
|
multiSelectionEnabled: false,
|
||||||
|
emptySelectionAllowed: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -652,39 +660,179 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildCollapsedView(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (widget.poll.title != null)
|
||||||
|
Text(
|
||||||
|
widget.poll.title!,
|
||||||
|
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
if (widget.poll.description != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 2),
|
||||||
|
child: Text(
|
||||||
|
widget.poll.description!,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.bodySmall?.color?.withOpacity(0.7),
|
||||||
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 2),
|
||||||
|
child: Text(
|
||||||
|
'${_questions.length} question${_questions.length == 1 ? '' : 's'}',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.bodySmall?.color?.withOpacity(0.7),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
_isCollapsed ? Icons.expand_more : Icons.expand_less,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_isCollapsed = !_isCollapsed;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
tooltip: _isCollapsed ? 'expandPoll'.tr() : 'collapsePoll'.tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (_questions.isEmpty) {
|
if (_questions.isEmpty) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If collapsed, show collapsed view for all states
|
||||||
|
if (_isCollapsed) {
|
||||||
|
return _buildCollapsedView(context);
|
||||||
|
}
|
||||||
|
|
||||||
// If poll is already submitted and not in readonly mode, and not in modification mode, show submitted view
|
// If poll is already submitted and not in readonly mode, and not in modification mode, show submitted view
|
||||||
if (widget.initialAnswers != null && !widget.isReadonly && !_isModifying) {
|
if (widget.initialAnswers != null && !widget.isReadonly && !_isModifying) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [_buildSubmittedView(context), _buildNavBar(context)],
|
children: [
|
||||||
|
_buildCollapsedView(context),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
transitionBuilder: (child, anim) {
|
||||||
|
final offset = Tween<Offset>(
|
||||||
|
begin: const Offset(0, -0.1),
|
||||||
|
end: Offset.zero,
|
||||||
|
).animate(CurvedAnimation(parent: anim, curve: Curves.easeOut));
|
||||||
|
final fade = CurvedAnimation(parent: anim, curve: Curves.easeOut);
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: fade,
|
||||||
|
child: SlideTransition(position: offset, child: child),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
key: const ValueKey('submitted_expanded'),
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [_buildSubmittedView(context), _buildNavBar(context)],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If poll is in readonly mode, show readonly view
|
// If poll is in readonly mode, show readonly view
|
||||||
if (widget.isReadonly) {
|
if (widget.isReadonly) {
|
||||||
return _buildReadonlyView(context);
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
_buildCollapsedView(context),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
transitionBuilder: (child, anim) {
|
||||||
|
final offset = Tween<Offset>(
|
||||||
|
begin: const Offset(0, -0.1),
|
||||||
|
end: Offset.zero,
|
||||||
|
).animate(CurvedAnimation(parent: anim, curve: Curves.easeOut));
|
||||||
|
final fade = CurvedAnimation(parent: anim, curve: Curves.easeOut);
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: fade,
|
||||||
|
child: SlideTransition(position: offset, child: child),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: _buildReadonlyView(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
_buildHeader(context),
|
_buildCollapsedView(context),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 8),
|
||||||
_AnimatedStep(
|
AnimatedSwitcher(
|
||||||
key: ValueKey(_current.id),
|
duration: const Duration(milliseconds: 300),
|
||||||
|
transitionBuilder: (child, anim) {
|
||||||
|
final offset = Tween<Offset>(
|
||||||
|
begin: const Offset(0, -0.1),
|
||||||
|
end: Offset.zero,
|
||||||
|
).animate(CurvedAnimation(parent: anim, curve: Curves.easeOut));
|
||||||
|
final fade = CurvedAnimation(parent: anim, curve: Curves.easeOut);
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: fade,
|
||||||
|
child: SlideTransition(position: offset, child: child),
|
||||||
|
);
|
||||||
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
|
key: const ValueKey('normal_expanded'),
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [_buildBody(context), _buildStats(context, _current)],
|
children: [
|
||||||
|
_buildHeader(context),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
_AnimatedStep(
|
||||||
|
key: ValueKey(_current.id),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
_buildBody(context),
|
||||||
|
_buildStats(context, _current),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
_buildNavBar(context),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
|
||||||
_buildNavBar(context),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
287
lib/widgets/post/compose_embed_sheet.dart
Normal file
287
lib/widgets/post/compose_embed_sheet.dart
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/models/post.dart';
|
||||||
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
|
import 'package:island/widgets/post/compose_shared.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
|
class ComposeEmbedSheet extends HookConsumerWidget {
|
||||||
|
final ComposeState state;
|
||||||
|
|
||||||
|
const ComposeEmbedSheet({super.key, required this.state});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final colorScheme = theme.colorScheme;
|
||||||
|
|
||||||
|
// Listen to embed view changes
|
||||||
|
final currentEmbedView = useValueListenable(state.embedView);
|
||||||
|
|
||||||
|
// Form state
|
||||||
|
final uriController = useTextEditingController();
|
||||||
|
final aspectRatioController = useTextEditingController();
|
||||||
|
final selectedRenderer = useState<PostEmbedViewRenderer>(
|
||||||
|
PostEmbedViewRenderer.webView,
|
||||||
|
);
|
||||||
|
|
||||||
|
void clearForm() {
|
||||||
|
uriController.clear();
|
||||||
|
aspectRatioController.clear();
|
||||||
|
selectedRenderer.value = PostEmbedViewRenderer.webView;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate form when embed view changes
|
||||||
|
useEffect(() {
|
||||||
|
if (currentEmbedView != null) {
|
||||||
|
uriController.text = currentEmbedView.uri;
|
||||||
|
aspectRatioController.text =
|
||||||
|
currentEmbedView.aspectRatio?.toString() ?? '';
|
||||||
|
selectedRenderer.value = currentEmbedView.renderer;
|
||||||
|
} else {
|
||||||
|
clearForm();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, [currentEmbedView]);
|
||||||
|
|
||||||
|
void saveEmbedView() {
|
||||||
|
final uri = uriController.text.trim();
|
||||||
|
if (uri.isEmpty) {
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
context,
|
||||||
|
).showSnackBar(SnackBar(content: Text('embedUriRequired'.tr())));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final aspectRatio =
|
||||||
|
aspectRatioController.text.trim().isNotEmpty
|
||||||
|
? double.tryParse(aspectRatioController.text.trim())
|
||||||
|
: null;
|
||||||
|
|
||||||
|
final embedView = SnPostEmbedView(
|
||||||
|
uri: uri,
|
||||||
|
aspectRatio: aspectRatio,
|
||||||
|
renderer: selectedRenderer.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (currentEmbedView != null) {
|
||||||
|
ComposeLogic.updateEmbedView(state, embedView);
|
||||||
|
} else {
|
||||||
|
ComposeLogic.setEmbedView(state, embedView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SheetScaffold(
|
||||||
|
titleText: 'embedView'.tr(),
|
||||||
|
heightFactor: 0.7,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
// Header with save button when editing
|
||||||
|
if (currentEmbedView != null)
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'editEmbed'.tr(),
|
||||||
|
style: theme.textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: saveEmbedView,
|
||||||
|
child: Text('save'.tr()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Content area
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Form fields
|
||||||
|
TextField(
|
||||||
|
controller: uriController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'embedUri'.tr(),
|
||||||
|
hintText: 'https://example.com',
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.url,
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
TextField(
|
||||||
|
controller: aspectRatioController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'aspectRatio'.tr(),
|
||||||
|
hintText: '16/9 = 1.777',
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.numberWithOptions(
|
||||||
|
decimal: true,
|
||||||
|
),
|
||||||
|
inputFormatters: [
|
||||||
|
FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*$')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
DropdownButtonFormField2<PostEmbedViewRenderer>(
|
||||||
|
value: selectedRenderer.value,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'renderer'.tr(),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
items:
|
||||||
|
PostEmbedViewRenderer.values.map((renderer) {
|
||||||
|
return DropdownMenuItem(
|
||||||
|
value: renderer,
|
||||||
|
child: Text(renderer.name).tr(),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
selectedRenderer.value = value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
// Current embed view display (when exists)
|
||||||
|
if (currentEmbedView != null) ...[
|
||||||
|
const Gap(32),
|
||||||
|
Text(
|
||||||
|
'currentEmbed'.tr(),
|
||||||
|
style: theme.textTheme.titleMedium,
|
||||||
|
).padding(horizontal: 4),
|
||||||
|
const Gap(8),
|
||||||
|
Card(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
bottom: 12,
|
||||||
|
top: 4,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
currentEmbedView.renderer ==
|
||||||
|
PostEmbedViewRenderer.webView
|
||||||
|
? Symbols.web
|
||||||
|
: Symbols.web,
|
||||||
|
color: colorScheme.primary,
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
currentEmbedView.uri,
|
||||||
|
style: theme.textTheme.bodyMedium,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Symbols.delete),
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder:
|
||||||
|
(dialogContext) => AlertDialog(
|
||||||
|
title: Text('deleteEmbed').tr(),
|
||||||
|
content:
|
||||||
|
Text('deleteEmbedConfirm').tr(),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed:
|
||||||
|
() =>
|
||||||
|
Navigator.of(
|
||||||
|
dialogContext,
|
||||||
|
).pop(),
|
||||||
|
child: Text('cancel'.tr()),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
ComposeLogic.deleteEmbedView(
|
||||||
|
state,
|
||||||
|
);
|
||||||
|
clearForm();
|
||||||
|
Navigator.of(
|
||||||
|
dialogContext,
|
||||||
|
).pop();
|
||||||
|
},
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor:
|
||||||
|
colorScheme.error,
|
||||||
|
),
|
||||||
|
child: Text('delete').tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
tooltip: 'delete'.tr(),
|
||||||
|
color: colorScheme.error,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
Text(
|
||||||
|
'aspectRatio'.tr(),
|
||||||
|
style: theme.textTheme.labelMedium?.copyWith(
|
||||||
|
color: colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Text(
|
||||||
|
currentEmbedView.aspectRatio != null
|
||||||
|
? currentEmbedView.aspectRatio!
|
||||||
|
.toStringAsFixed(2)
|
||||||
|
: 'notSet'.tr(),
|
||||||
|
style: theme.textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
] else ...[
|
||||||
|
// Save button for new embed
|
||||||
|
const Gap(16),
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: FilledButton.icon(
|
||||||
|
onPressed: saveEmbedView,
|
||||||
|
icon: const Icon(Symbols.add),
|
||||||
|
label: Text('addEmbed'.tr()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@ class ComposeState {
|
|||||||
final ValueNotifier<List<SnPostCategory>> categories;
|
final ValueNotifier<List<SnPostCategory>> categories;
|
||||||
StringTagController tagsController;
|
StringTagController tagsController;
|
||||||
final ValueNotifier<SnRealm?> realm;
|
final ValueNotifier<SnRealm?> realm;
|
||||||
|
final ValueNotifier<SnPostEmbedView?> embedView;
|
||||||
final String draftId;
|
final String draftId;
|
||||||
int postType;
|
int postType;
|
||||||
// Linked poll id for this compose session (nullable)
|
// Linked poll id for this compose session (nullable)
|
||||||
@@ -56,6 +57,7 @@ class ComposeState {
|
|||||||
required this.tagsController,
|
required this.tagsController,
|
||||||
required this.categories,
|
required this.categories,
|
||||||
required this.realm,
|
required this.realm,
|
||||||
|
required this.embedView,
|
||||||
required this.draftId,
|
required this.draftId,
|
||||||
this.postType = 0,
|
this.postType = 0,
|
||||||
String? pollId,
|
String? pollId,
|
||||||
@@ -120,6 +122,7 @@ class ComposeLogic {
|
|||||||
originalPost?.categories ?? [],
|
originalPost?.categories ?? [],
|
||||||
),
|
),
|
||||||
realm: ValueNotifier(originalPost?.realm),
|
realm: ValueNotifier(originalPost?.realm),
|
||||||
|
embedView: ValueNotifier<SnPostEmbedView?>(originalPost?.embedView),
|
||||||
draftId: id,
|
draftId: id,
|
||||||
postType: postType,
|
postType: postType,
|
||||||
// initialize without poll by default
|
// initialize without poll by default
|
||||||
@@ -151,6 +154,7 @@ class ComposeLogic {
|
|||||||
tagsController: tagsController,
|
tagsController: tagsController,
|
||||||
categories: ValueNotifier<List<SnPostCategory>>([]),
|
categories: ValueNotifier<List<SnPostCategory>>([]),
|
||||||
realm: ValueNotifier(null),
|
realm: ValueNotifier(null),
|
||||||
|
embedView: ValueNotifier<SnPostEmbedView?>(draft.embedView),
|
||||||
draftId: draft.id,
|
draftId: draft.id,
|
||||||
postType: postType,
|
postType: postType,
|
||||||
pollId: null,
|
pollId: null,
|
||||||
@@ -169,10 +173,6 @@ class ComposeLogic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (state._autoSaveTimer == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload any local attachments first
|
// Upload any local attachments first
|
||||||
final baseUrl = ref.watch(serverUrlProvider);
|
final baseUrl = ref.watch(serverUrlProvider);
|
||||||
final token = await getToken(ref.watch(tokenProvider));
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
@@ -253,6 +253,7 @@ class ComposeLogic {
|
|||||||
tags: [],
|
tags: [],
|
||||||
categories: [],
|
categories: [],
|
||||||
collections: [],
|
collections: [],
|
||||||
|
embedView: state.embedView.value,
|
||||||
createdAt: DateTime.now(),
|
createdAt: DateTime.now(),
|
||||||
updatedAt: DateTime.now(),
|
updatedAt: DateTime.now(),
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
@@ -279,10 +280,6 @@ class ComposeLogic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (state._autoSaveTimer == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final draft = SnPost(
|
final draft = SnPost(
|
||||||
id: state.draftId,
|
id: state.draftId,
|
||||||
title: state.titleController.text,
|
title: state.titleController.text,
|
||||||
@@ -329,6 +326,7 @@ class ComposeLogic {
|
|||||||
tags: [],
|
tags: [],
|
||||||
categories: [],
|
categories: [],
|
||||||
collections: [],
|
collections: [],
|
||||||
|
embedView: state.embedView.value,
|
||||||
createdAt: DateTime.now(),
|
createdAt: DateTime.now(),
|
||||||
updatedAt: DateTime.now(),
|
updatedAt: DateTime.now(),
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
@@ -577,6 +575,18 @@ class ComposeLogic {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setEmbedView(ComposeState state, SnPostEmbedView embedView) {
|
||||||
|
state.embedView.value = embedView;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void updateEmbedView(ComposeState state, SnPostEmbedView embedView) {
|
||||||
|
state.embedView.value = embedView;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void deleteEmbedView(ComposeState state) {
|
||||||
|
state.embedView.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
static Future<void> pickPoll(
|
static Future<void> pickPoll(
|
||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
ComposeState state,
|
ComposeState state,
|
||||||
@@ -660,6 +670,8 @@ class ComposeLogic {
|
|||||||
'categories': state.categories.value.map((e) => e.slug).toList(),
|
'categories': state.categories.value.map((e) => e.slug).toList(),
|
||||||
if (state.realm.value != null) 'realm_id': state.realm.value?.id,
|
if (state.realm.value != null) 'realm_id': state.realm.value?.id,
|
||||||
if (state.pollId.value != null) 'poll_id': state.pollId.value,
|
if (state.pollId.value != null) 'poll_id': state.pollId.value,
|
||||||
|
if (state.embedView.value != null)
|
||||||
|
'embed_view': state.embedView.value!.toJson(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send request
|
// Send request
|
||||||
@@ -753,6 +765,7 @@ class ComposeLogic {
|
|||||||
state.tagsController.dispose();
|
state.tagsController.dispose();
|
||||||
state.categories.dispose();
|
state.categories.dispose();
|
||||||
state.realm.dispose();
|
state.realm.dispose();
|
||||||
|
state.embedView.dispose();
|
||||||
state.pollId.dispose();
|
state.pollId.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/models/post.dart';
|
import 'package:island/models/post.dart';
|
||||||
import 'package:island/services/compose_storage_db.dart';
|
import 'package:island/services/compose_storage_db.dart';
|
||||||
|
import 'package:island/widgets/post/compose_embed_sheet.dart';
|
||||||
import 'package:island/widgets/post/compose_shared.dart';
|
import 'package:island/widgets/post/compose_shared.dart';
|
||||||
import 'package:island/widgets/post/draft_manager.dart';
|
import 'package:island/widgets/post/draft_manager.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
@@ -33,13 +34,21 @@ class ComposeToolbar extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void saveDraft() {
|
void saveDraft() {
|
||||||
ComposeLogic.saveDraft(ref, state);
|
ComposeLogic.saveDraftManually(ref, state, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pickPoll() {
|
void pickPoll() {
|
||||||
ComposeLogic.pickPoll(ref, state, context);
|
ComposeLogic.pickPoll(ref, state, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void showEmbedSheet() {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (context) => ComposeEmbedSheet(state: state),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void showDraftManager() {
|
void showDraftManager() {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -112,6 +121,25 @@ class ComposeToolbar extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
// Embed button with visual state when embed is present
|
||||||
|
ListenableBuilder(
|
||||||
|
listenable: state.embedView,
|
||||||
|
builder: (context, _) {
|
||||||
|
return IconButton(
|
||||||
|
onPressed: showEmbedSheet,
|
||||||
|
icon: const Icon(Symbols.web),
|
||||||
|
tooltip: 'embedView'.tr(),
|
||||||
|
color: colorScheme.primary,
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: WidgetStatePropertyAll(
|
||||||
|
state.embedView.value != null
|
||||||
|
? colorScheme.primary.withOpacity(0.15)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
if (originalPost == null && state.isEmpty)
|
if (originalPost == null && state.isEmpty)
|
||||||
IconButton(
|
IconButton(
|
||||||
|
|||||||
@@ -16,138 +16,145 @@ class DraftManagerSheet extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final colorScheme = theme.colorScheme;
|
final colorScheme = theme.colorScheme;
|
||||||
final isLoading = useState(true);
|
final searchController = useTextEditingController();
|
||||||
|
final searchQuery = useState('');
|
||||||
|
|
||||||
final drafts = ref.watch(composeStorageNotifierProvider);
|
final drafts = ref.watch(composeStorageNotifierProvider);
|
||||||
|
|
||||||
// Track loading state based on drafts being loaded
|
// Search functionality
|
||||||
useEffect(() {
|
final filteredDrafts = useMemoized(() {
|
||||||
// Set loading to false after drafts are loaded
|
if (searchQuery.value.isEmpty) {
|
||||||
// We consider drafts loaded when the provider has been initialized
|
return drafts.values.toList()
|
||||||
Future.microtask(() {
|
..sort((a, b) => b.updatedAt!.compareTo(a.updatedAt!));
|
||||||
if (isLoading.value) {
|
}
|
||||||
isLoading.value = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return null;
|
|
||||||
}, [drafts]);
|
|
||||||
|
|
||||||
final sortedDrafts = useMemoized(
|
final query = searchQuery.value.toLowerCase();
|
||||||
() {
|
return drafts.values.where((draft) {
|
||||||
final draftList = drafts.values.toList();
|
return (draft.title?.toLowerCase().contains(query) ?? false) ||
|
||||||
draftList.sort((a, b) => b.updatedAt!.compareTo(a.updatedAt!));
|
(draft.description?.toLowerCase().contains(query) ?? false) ||
|
||||||
return draftList;
|
(draft.content?.toLowerCase().contains(query) ?? false);
|
||||||
},
|
}).toList()
|
||||||
[
|
..sort((a, b) => b.updatedAt!.compareTo(a.updatedAt!));
|
||||||
drafts.length,
|
}, [drafts, searchQuery.value]);
|
||||||
drafts.values.map((e) => e.updatedAt!.millisecondsSinceEpoch).join(),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
return SheetScaffold(
|
return SheetScaffold(
|
||||||
titleText: 'drafts'.tr(),
|
titleText: 'drafts'.tr(),
|
||||||
child:
|
child: Column(
|
||||||
isLoading.value
|
children: [
|
||||||
? const Center(child: CircularProgressIndicator())
|
// Search bar
|
||||||
: Column(
|
Padding(
|
||||||
children: [
|
padding: const EdgeInsets.all(16),
|
||||||
if (sortedDrafts.isEmpty)
|
child: TextField(
|
||||||
Expanded(
|
controller: searchController,
|
||||||
child: Center(
|
decoration: InputDecoration(
|
||||||
child: Column(
|
hintText: 'searchDrafts'.tr(),
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
prefixIcon: const Icon(Symbols.search),
|
||||||
children: [
|
border: OutlineInputBorder(
|
||||||
Icon(
|
borderRadius: BorderRadius.circular(12),
|
||||||
Symbols.draft,
|
),
|
||||||
size: 64,
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
color: colorScheme.onSurface.withOpacity(0.3),
|
horizontal: 16,
|
||||||
),
|
vertical: 12,
|
||||||
const Gap(16),
|
),
|
||||||
Text(
|
),
|
||||||
'noDrafts'.tr(),
|
onChanged: (value) => searchQuery.value = value,
|
||||||
style: theme.textTheme.bodyLarge?.copyWith(
|
),
|
||||||
color: colorScheme.onSurface.withOpacity(0.6),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Expanded(
|
|
||||||
child: ListView.builder(
|
|
||||||
itemCount: sortedDrafts.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final draft = sortedDrafts[index];
|
|
||||||
return _DraftItem(
|
|
||||||
draft: draft,
|
|
||||||
onTap: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
onDraftSelected?.call(draft.id);
|
|
||||||
},
|
|
||||||
onDelete: () async {
|
|
||||||
await ref
|
|
||||||
.read(composeStorageNotifierProvider.notifier)
|
|
||||||
.deleteDraft(draft.id);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (sortedDrafts.isNotEmpty) ...[
|
|
||||||
const Divider(),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: OutlinedButton.icon(
|
|
||||||
onPressed: () async {
|
|
||||||
final confirmed = await showDialog<bool>(
|
|
||||||
context: context,
|
|
||||||
builder:
|
|
||||||
(context) => AlertDialog(
|
|
||||||
title: Text('clearAllDrafts'.tr()),
|
|
||||||
content: Text(
|
|
||||||
'clearAllDraftsConfirm'.tr(),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed:
|
|
||||||
() => Navigator.of(
|
|
||||||
context,
|
|
||||||
).pop(false),
|
|
||||||
child: Text('cancel'.tr()),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed:
|
|
||||||
() => Navigator.of(
|
|
||||||
context,
|
|
||||||
).pop(true),
|
|
||||||
child: Text('confirm'.tr()),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (confirmed == true) {
|
// Drafts list
|
||||||
await ref
|
if (filteredDrafts.isEmpty)
|
||||||
.read(
|
Expanded(
|
||||||
composeStorageNotifierProvider.notifier,
|
child: Center(
|
||||||
)
|
child: Column(
|
||||||
.clearAllDrafts();
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
}
|
children: [
|
||||||
},
|
Icon(
|
||||||
icon: const Icon(Symbols.delete_sweep),
|
Symbols.draft,
|
||||||
label: Text('clearAll'.tr()),
|
size: 64,
|
||||||
),
|
color: colorScheme.onSurface.withOpacity(0.3),
|
||||||
),
|
),
|
||||||
],
|
const Gap(16),
|
||||||
|
Text(
|
||||||
|
searchQuery.value.isEmpty
|
||||||
|
? 'noDrafts'.tr()
|
||||||
|
: 'noSearchResults'.tr(),
|
||||||
|
style: theme.textTheme.bodyLarge?.copyWith(
|
||||||
|
color: colorScheme.onSurface.withOpacity(0.6),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: filteredDrafts.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final draft = filteredDrafts[index];
|
||||||
|
return _DraftItem(
|
||||||
|
draft: draft,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
onDraftSelected?.call(draft.id);
|
||||||
|
},
|
||||||
|
onDelete: () async {
|
||||||
|
await ref
|
||||||
|
.read(composeStorageNotifierProvider.notifier)
|
||||||
|
.deleteDraft(draft.id);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Clear all button
|
||||||
|
if (filteredDrafts.isNotEmpty) ...[
|
||||||
|
const Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: OutlinedButton.icon(
|
||||||
|
onPressed: () async {
|
||||||
|
final confirmed = await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder:
|
||||||
|
(context) => AlertDialog(
|
||||||
|
title: Text('clearAllDrafts'.tr()),
|
||||||
|
content: Text('clearAllDraftsConfirm'.tr()),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed:
|
||||||
|
() => Navigator.of(context).pop(false),
|
||||||
|
child: Text('cancel'.tr()),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed:
|
||||||
|
() => Navigator.of(context).pop(true),
|
||||||
|
child: Text('confirm'.tr()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (confirmed == true) {
|
||||||
|
await ref
|
||||||
|
.read(composeStorageNotifierProvider.notifier)
|
||||||
|
.clearAllDrafts();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: const Icon(Symbols.delete_sweep),
|
||||||
|
label: Text('clearAll'.tr()),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,7 +176,7 @@ class _DraftItem extends StatelessWidget {
|
|||||||
final preview =
|
final preview =
|
||||||
content.length > 100 ? '${content.substring(0, 100)}...' : content;
|
content.length > 100 ? '${content.substring(0, 100)}...' : content;
|
||||||
final timeAgo = _formatTimeAgo(draft.updatedAt!);
|
final timeAgo = _formatTimeAgo(draft.updatedAt!);
|
||||||
final visibility = _parseVisibility(draft.visibility);
|
final visibility = _parseVisibility(draft.visibility).tr();
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||||
|
|||||||
291
lib/widgets/post/embed_view_renderer.dart
Normal file
291
lib/widgets/post/embed_view_renderer.dart
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/models/post.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
class EmbedViewRenderer extends HookConsumerWidget {
|
||||||
|
final SnPostEmbedView embedView;
|
||||||
|
final double? maxHeight;
|
||||||
|
final BorderRadius? borderRadius;
|
||||||
|
|
||||||
|
const EmbedViewRenderer({
|
||||||
|
super.key,
|
||||||
|
required this.embedView,
|
||||||
|
this.maxHeight = 400,
|
||||||
|
this.borderRadius,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final colorScheme = theme.colorScheme;
|
||||||
|
|
||||||
|
// State management for lazy loading
|
||||||
|
final shouldLoad = useState(false);
|
||||||
|
final isLoading = useState(false);
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
constraints: BoxConstraints(maxHeight: maxHeight ?? 400),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: borderRadius ?? BorderRadius.circular(12),
|
||||||
|
border: Border.all(
|
||||||
|
color: colorScheme.outline.withOpacity(0.3),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
color: colorScheme.surfaceContainerLowest,
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: borderRadius ?? BorderRadius.circular(12),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
// Header with embed info
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colorScheme.surfaceContainerHigh,
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: colorScheme.outline.withOpacity(0.2),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
embedView.renderer == PostEmbedViewRenderer.webView
|
||||||
|
? Symbols.web
|
||||||
|
: Symbols.web,
|
||||||
|
size: 16,
|
||||||
|
color: colorScheme.primary,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
_getUriDisplay(embedView.uri),
|
||||||
|
style: theme.textTheme.bodySmall?.copyWith(
|
||||||
|
color: colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Symbols.open_in_new,
|
||||||
|
size: 16,
|
||||||
|
color: colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
final uri = Uri.parse(embedView.uri);
|
||||||
|
if (await canLaunchUrl(uri)) {
|
||||||
|
await launchUrl(
|
||||||
|
uri,
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
tooltip: 'Open in browser',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// WebView content with lazy loading
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: embedView.aspectRatio ?? 1,
|
||||||
|
child:
|
||||||
|
shouldLoad.value
|
||||||
|
? Stack(
|
||||||
|
children: [
|
||||||
|
InAppWebView(
|
||||||
|
initialUrlRequest: URLRequest(
|
||||||
|
url: WebUri(embedView.uri),
|
||||||
|
),
|
||||||
|
initialSettings: InAppWebViewSettings(
|
||||||
|
javaScriptEnabled: true,
|
||||||
|
mediaPlaybackRequiresUserGesture: false,
|
||||||
|
allowsInlineMediaPlayback: true,
|
||||||
|
useShouldOverrideUrlLoading: true,
|
||||||
|
useOnLoadResource: true,
|
||||||
|
supportZoom: false,
|
||||||
|
useWideViewPort: false,
|
||||||
|
loadWithOverviewMode: true,
|
||||||
|
builtInZoomControls: false,
|
||||||
|
displayZoomControls: false,
|
||||||
|
minimumFontSize: 12,
|
||||||
|
preferredContentMode:
|
||||||
|
UserPreferredContentMode.RECOMMENDED,
|
||||||
|
allowsBackForwardNavigationGestures: false,
|
||||||
|
allowsLinkPreview: false,
|
||||||
|
isInspectable: false,
|
||||||
|
),
|
||||||
|
onWebViewCreated: (controller) {
|
||||||
|
// Configure webview settings
|
||||||
|
controller.addJavaScriptHandler(
|
||||||
|
handlerName: 'onHeightChanged',
|
||||||
|
callback: (args) {
|
||||||
|
// Handle dynamic height changes if needed
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onLoadStart: (controller, url) {
|
||||||
|
isLoading.value = true;
|
||||||
|
},
|
||||||
|
onLoadStop: (controller, url) async {
|
||||||
|
isLoading.value = false;
|
||||||
|
// Inject CSS to improve mobile display and remove borders
|
||||||
|
await controller.evaluateJavascript(
|
||||||
|
source: '''
|
||||||
|
// Remove unwanted elements
|
||||||
|
var elements = document.querySelectorAll('nav, header, footer, .ads, .advertisement, .sidebar');
|
||||||
|
for (var i = 0; i < elements.length; i++) {
|
||||||
|
elements[i].style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove borders from embedded content (YouTube, Vimeo, etc.)
|
||||||
|
var iframes = document.querySelectorAll('iframe');
|
||||||
|
for (var i = 0; i < iframes.length; i++) {
|
||||||
|
iframes[i].style.border = 'none';
|
||||||
|
iframes[i].style.borderRadius = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove borders from video elements
|
||||||
|
var videos = document.querySelectorAll('video');
|
||||||
|
for (var i = 0; i < videos.length; i++) {
|
||||||
|
videos[i].style.border = 'none';
|
||||||
|
videos[i].style.borderRadius = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove borders from any element that might have them
|
||||||
|
var allElements = document.querySelectorAll('*');
|
||||||
|
for (var i = 0; i < allElements.length; i++) {
|
||||||
|
if (allElements[i].style.border) {
|
||||||
|
allElements[i].style.border = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Improve readability
|
||||||
|
var body = document.body;
|
||||||
|
body.style.fontSize = '14px';
|
||||||
|
body.style.lineHeight = '1.4';
|
||||||
|
body.style.margin = '0';
|
||||||
|
body.style.padding = '0';
|
||||||
|
|
||||||
|
// Handle dynamic content
|
||||||
|
var observer = new MutationObserver(function(mutations) {
|
||||||
|
// Remove borders from newly added elements
|
||||||
|
var newIframes = document.querySelectorAll('iframe');
|
||||||
|
for (var i = 0; i < newIframes.length; i++) {
|
||||||
|
if (!newIframes[i].style.border || newIframes[i].style.border !== 'none') {
|
||||||
|
newIframes[i].style.border = 'none';
|
||||||
|
newIframes[i].style.borderRadius = '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var newVideos = document.querySelectorAll('video');
|
||||||
|
for (var i = 0; i < newVideos.length; i++) {
|
||||||
|
if (!newVideos[i].style.border || newVideos[i].style.border !== 'none') {
|
||||||
|
newVideos[i].style.border = 'none';
|
||||||
|
newVideos[i].style.borderRadius = '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.flutter_inappwebview.callHandler('onHeightChanged', document.body.scrollHeight);
|
||||||
|
});
|
||||||
|
observer.observe(document.body, { childList: true, subtree: true });
|
||||||
|
''',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onLoadError: (controller, url, code, message) {
|
||||||
|
isLoading.value = false;
|
||||||
|
},
|
||||||
|
onLoadHttpError: (
|
||||||
|
controller,
|
||||||
|
url,
|
||||||
|
statusCode,
|
||||||
|
description,
|
||||||
|
) {
|
||||||
|
isLoading.value = false;
|
||||||
|
},
|
||||||
|
shouldOverrideUrlLoading: (
|
||||||
|
controller,
|
||||||
|
navigationAction,
|
||||||
|
) async {
|
||||||
|
final uri = navigationAction.request.url;
|
||||||
|
if (uri != null &&
|
||||||
|
uri.toString() != embedView.uri) {
|
||||||
|
// Open external links in browser
|
||||||
|
// You might want to use url_launcher here
|
||||||
|
return NavigationActionPolicy.CANCEL;
|
||||||
|
}
|
||||||
|
return NavigationActionPolicy.ALLOW;
|
||||||
|
},
|
||||||
|
onProgressChanged: (controller, progress) {
|
||||||
|
// Handle progress changes if needed
|
||||||
|
},
|
||||||
|
onConsoleMessage: (controller, consoleMessage) {
|
||||||
|
// Handle console messages for debugging
|
||||||
|
debugPrint(
|
||||||
|
'WebView Console: ${consoleMessage.message}',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (isLoading.value)
|
||||||
|
Container(
|
||||||
|
color: colorScheme.surfaceContainerLowest,
|
||||||
|
child: const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
shouldLoad.value = true;
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
color: colorScheme.surfaceContainerLowest,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Symbols.play_arrow,
|
||||||
|
size: 48,
|
||||||
|
color: colorScheme.onSurfaceVariant.withOpacity(
|
||||||
|
0.6,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'Tap to load content',
|
||||||
|
style: theme.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: colorScheme.onSurfaceVariant
|
||||||
|
.withOpacity(0.6),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getUriDisplay(String uri) {
|
||||||
|
try {
|
||||||
|
final parsedUri = Uri.parse(uri);
|
||||||
|
return parsedUri.host.isNotEmpty ? parsedUri.host : uri;
|
||||||
|
} catch (e) {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
287
lib/widgets/post/post_award_sheet.dart
Normal file
287
lib/widgets/post/post_award_sheet.dart
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/models/post.dart';
|
||||||
|
import 'package:island/models/wallet.dart';
|
||||||
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:island/widgets/alert.dart';
|
||||||
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
|
import 'package:island/widgets/payment/payment_overlay.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
|
||||||
|
class PostAwardSheet extends HookConsumerWidget {
|
||||||
|
final SnPost post;
|
||||||
|
const PostAwardSheet({super.key, required this.post});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final messageController = useTextEditingController();
|
||||||
|
final amountController = useTextEditingController();
|
||||||
|
final selectedAttitude = useState<int>(0); // 0 for positive, 2 for negative
|
||||||
|
|
||||||
|
return SheetScaffold(
|
||||||
|
titleText: 'awardPost'.tr(),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
// Post Preview Section
|
||||||
|
_buildPostPreview(context),
|
||||||
|
const Gap(20),
|
||||||
|
|
||||||
|
// Award Result Explanation
|
||||||
|
_buildAwardResultExplanation(context),
|
||||||
|
const Gap(20),
|
||||||
|
|
||||||
|
Text(
|
||||||
|
'awardMessage'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
TextField(
|
||||||
|
controller: messageController,
|
||||||
|
maxLines: 3,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'awardMessageHint'.tr(),
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
Text(
|
||||||
|
'awardAttitude'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
SegmentedButton<int>(
|
||||||
|
segments: [
|
||||||
|
ButtonSegment<int>(
|
||||||
|
value: 0,
|
||||||
|
label: Text('awardAttitudePositive'.tr()),
|
||||||
|
icon: const Icon(Symbols.thumb_up),
|
||||||
|
),
|
||||||
|
ButtonSegment<int>(
|
||||||
|
value: 2,
|
||||||
|
label: Text('awardAttitudeNegative'.tr()),
|
||||||
|
icon: const Icon(Symbols.thumb_down),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
selected: {selectedAttitude.value},
|
||||||
|
onSelectionChanged: (Set<int> selection) {
|
||||||
|
selectedAttitude.value = selection.first;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
Text(
|
||||||
|
'awardAmount'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
TextField(
|
||||||
|
controller: amountController,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'awardAmountHint'.tr(),
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
|
),
|
||||||
|
suffixText: 'NSP',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(24),
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: FilledButton.icon(
|
||||||
|
onPressed:
|
||||||
|
() => _submitAward(
|
||||||
|
context,
|
||||||
|
ref,
|
||||||
|
messageController,
|
||||||
|
amountController,
|
||||||
|
selectedAttitude.value,
|
||||||
|
),
|
||||||
|
icon: const Icon(Symbols.star),
|
||||||
|
label: Text('awardSubmit'.tr()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPostPreview(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Symbols.article,
|
||||||
|
size: 20,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Text(
|
||||||
|
'awardPostPreview'.tr(),
|
||||||
|
style: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Text(
|
||||||
|
post.content ?? 'awardNoContent'.tr(),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
...[
|
||||||
|
const Gap(4),
|
||||||
|
Row(
|
||||||
|
spacing: 6,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'awardByPublisher'.tr(args: ['@${post.publisher.name}']),
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ProfilePictureWidget(file: post.publisher.picture, radius: 8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildAwardResultExplanation(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.primaryContainer.withOpacity(0.3),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).colorScheme.primary.withOpacity(0.2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Symbols.info,
|
||||||
|
size: 20,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Text(
|
||||||
|
'awardBenefits'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Text(
|
||||||
|
'awardBenefitsDescription'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _submitAward(
|
||||||
|
BuildContext context,
|
||||||
|
WidgetRef ref,
|
||||||
|
TextEditingController messageController,
|
||||||
|
TextEditingController amountController,
|
||||||
|
int selectedAttitude,
|
||||||
|
) async {
|
||||||
|
// Get values from controllers
|
||||||
|
final message = messageController.text.trim();
|
||||||
|
final amountText = amountController.text.trim();
|
||||||
|
|
||||||
|
// Validate inputs
|
||||||
|
if (amountText.isEmpty) {
|
||||||
|
showSnackBar('awardAmountRequired'.tr());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final amount = double.tryParse(amountText);
|
||||||
|
if (amount == null || amount <= 0) {
|
||||||
|
showSnackBar('awardAmountInvalid'.tr());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.length > 4096) {
|
||||||
|
showSnackBar('awardMessageTooLong'.tr());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
showLoadingModal(context);
|
||||||
|
|
||||||
|
final client = ref.read(apiClientProvider);
|
||||||
|
|
||||||
|
// Send award request
|
||||||
|
final awardResponse = await client.post(
|
||||||
|
'/sphere/posts/${post.id}/awards',
|
||||||
|
data: {
|
||||||
|
'amount': amount,
|
||||||
|
'attitude': selectedAttitude,
|
||||||
|
if (message.isNotEmpty) 'message': message,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final orderId = awardResponse.data['order_id'] as String;
|
||||||
|
|
||||||
|
// Fetch order details
|
||||||
|
final orderResponse = await client.get('/id/orders/$orderId');
|
||||||
|
final order = SnWalletOrder.fromJson(orderResponse.data);
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
hideLoadingModal(context);
|
||||||
|
|
||||||
|
// Show payment overlay
|
||||||
|
final paidOrder = await PaymentOverlay.show(
|
||||||
|
context: context,
|
||||||
|
order: order,
|
||||||
|
enableBiometric: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (paidOrder != null && context.mounted) {
|
||||||
|
showSnackBar('awardSuccess'.tr());
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (context.mounted) {
|
||||||
|
hideLoadingModal(context);
|
||||||
|
showErrorAlert(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,8 +18,10 @@ import 'package:island/screens/posts/compose.dart';
|
|||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:island/widgets/content/markdown.dart';
|
import 'package:island/widgets/content/markdown.dart';
|
||||||
import 'package:island/widgets/post/post_item_screenshot.dart';
|
import 'package:island/widgets/post/post_item_screenshot.dart';
|
||||||
|
import 'package:island/widgets/post/post_award_sheet.dart';
|
||||||
import 'package:island/widgets/post/post_pin_sheet.dart';
|
import 'package:island/widgets/post/post_pin_sheet.dart';
|
||||||
import 'package:island/widgets/post/post_shared.dart';
|
import 'package:island/widgets/post/post_shared.dart';
|
||||||
|
import 'package:island/widgets/post/embed_view_renderer.dart';
|
||||||
import 'package:island/widgets/safety/abuse_report_helper.dart';
|
import 'package:island/widgets/safety/abuse_report_helper.dart';
|
||||||
import 'package:island/widgets/share/share_sheet.dart';
|
import 'package:island/widgets/share/share_sheet.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
@@ -245,6 +247,18 @@ class PostActionableItem extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
MenuAction(
|
||||||
|
title: 'award'.tr(),
|
||||||
|
image: MenuImage.icon(Symbols.star),
|
||||||
|
callback: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
useRootNavigator: true,
|
||||||
|
builder: (context) => PostAwardSheet(post: item),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
MenuSeparator(),
|
MenuSeparator(),
|
||||||
MenuAction(
|
MenuAction(
|
||||||
title: 'share'.tr(),
|
title: 'share'.tr(),
|
||||||
@@ -422,6 +436,7 @@ class PostItem extends HookConsumerWidget {
|
|||||||
MarkdownTextContent(
|
MarkdownTextContent(
|
||||||
content: translatedText.value!,
|
content: translatedText.value!,
|
||||||
isSelectable: isTextSelectable,
|
isSelectable: isTextSelectable,
|
||||||
|
attachments: item.attachments,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -536,6 +551,12 @@ class PostItem extends HookConsumerWidget {
|
|||||||
translationSection: translationSection,
|
translationSection: translationSection,
|
||||||
renderingPadding: renderingPadding,
|
renderingPadding: renderingPadding,
|
||||||
),
|
),
|
||||||
|
if (item.embedView != null)
|
||||||
|
EmbedViewRenderer(
|
||||||
|
embedView: item.embedView!,
|
||||||
|
maxHeight: 400,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
).padding(horizontal: renderingPadding.horizontal, vertical: 8),
|
||||||
if (isShowReference)
|
if (isShowReference)
|
||||||
ReferencedPostWidget(item: item, renderingPadding: renderingPadding),
|
ReferencedPostWidget(item: item, renderingPadding: renderingPadding),
|
||||||
if (item.repliesCount > 0 && isEmbedReply)
|
if (item.repliesCount > 0 && isEmbedReply)
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ class PostReplyPreview extends HookConsumerWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: MarkdownTextContent(
|
child: MarkdownTextContent(
|
||||||
content: post.content!,
|
content: post.content!,
|
||||||
|
attachments: post.attachments,
|
||||||
).padding(top: 2),
|
).padding(top: 2),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
@@ -210,6 +211,7 @@ class PostReplyPreview extends HookConsumerWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: MarkdownTextContent(
|
child: MarkdownTextContent(
|
||||||
content: data.value!.content!,
|
content: data.value!.content!,
|
||||||
|
attachments: data.value!.attachments,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
@@ -745,7 +747,10 @@ class PostBody extends ConsumerWidget {
|
|||||||
style: Theme.of(context).textTheme.bodyMedium,
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
MarkdownTextContent(content: '${item.content!}...'),
|
MarkdownTextContent(
|
||||||
|
content: '${item.content!}...',
|
||||||
|
attachments: item.attachments,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -784,6 +789,7 @@ class PostBody extends ConsumerWidget {
|
|||||||
? '${item.content!}...'
|
? '${item.content!}...'
|
||||||
: item.content ?? '',
|
: item.content ?? '',
|
||||||
isSelectable: isTextSelectable,
|
isSelectable: isTextSelectable,
|
||||||
|
attachments: item.attachments,
|
||||||
),
|
),
|
||||||
if (translationSection != null) translationSection!,
|
if (translationSection != null) translationSection!,
|
||||||
],
|
],
|
||||||
@@ -900,6 +906,7 @@ class PostBody extends ConsumerWidget {
|
|||||||
poll: SnPollWithStats.fromJson(embedData['poll']),
|
poll: SnPollWithStats.fromJson(embedData['poll']),
|
||||||
onSubmit: (_) {},
|
onSubmit: (_) {},
|
||||||
isReadonly: !isInteractive,
|
isReadonly: !isInteractive,
|
||||||
|
isInitiallyExpanded: isFullPost,
|
||||||
).padding(horizontal: 16, vertical: 12),
|
).padding(horizontal: 16, vertical: 12),
|
||||||
),
|
),
|
||||||
_ => Text('Unable show embed: ${embedData['type']}'),
|
_ => Text('Unable show embed: ${embedData['type']}'),
|
||||||
|
|||||||
@@ -2206,7 +2206,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.1"
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shelf
|
name: shelf
|
||||||
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
|
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
|
||||||
@@ -2214,13 +2214,13 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.2"
|
version: "1.4.2"
|
||||||
shelf_web_socket:
|
shelf_web_socket:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shelf_web_socket
|
name: shelf_web_socket
|
||||||
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
|
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "2.0.1"
|
||||||
shortid:
|
shortid:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -145,6 +145,8 @@ dependencies:
|
|||||||
flutter_local_notifications: ^19.4.1
|
flutter_local_notifications: ^19.4.1
|
||||||
wakelock_plus: ^1.3.2
|
wakelock_plus: ^1.3.2
|
||||||
slide_countdown: ^2.0.2
|
slide_countdown: ^2.0.2
|
||||||
|
shelf: ^1.4.2
|
||||||
|
shelf_web_socket: ^2.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user