Compare commits

..

83 Commits

Author SHA1 Message Date
LittleSheep
4dca6189cb 💄 Optimize post category subscribe loading state 2025-08-25 16:17:19 +08:00
LittleSheep
c7f5b63fe5 Subscribe to category and tags 2025-08-25 16:15:51 +08:00
LittleSheep
96c2f45c85 💄 Optimize articles page 2025-08-25 15:33:27 +08:00
LittleSheep
06f04eb3a5 🎉 Launch 3.2.0+128 2025-08-25 01:55:54 +08:00
LittleSheep
8af97e43b4 💄 Optimize articles view 2025-08-25 01:48:43 +08:00
LittleSheep
d1e8234b93 🐛 Fix bugs 2025-08-24 23:50:36 +08:00
LittleSheep
a03d6015a6 Manage secret 2025-08-24 23:46:14 +08:00
LittleSheep
246ac52d0a Custom app detail page 2025-08-24 22:33:41 +08:00
LittleSheep
abf395ff9a 🐛 Dozens of bug fixes 2025-08-24 21:49:40 +08:00
LittleSheep
4fdc8eb1d0 Feed discover and subscription 2025-08-24 13:55:06 +08:00
LittleSheep
d7dcde898c 🐛 Dozens of bug fixes 2025-08-24 02:33:02 +08:00
LittleSheep
f85484d3ed 💄 Optimize for large screen 2025-08-24 02:28:16 +08:00
LittleSheep
5060bd30c9 Rotate bot key 2025-08-24 01:49:56 +08:00
LittleSheep
3959f2260b Bot key management 2025-08-23 23:35:37 +08:00
LittleSheep
6f4f1216ad 🐛 Fix project detail 2025-08-23 17:45:08 +08:00
LittleSheep
f401ffbf81 🐛 Fix profile page 2025-08-23 17:34:01 +08:00
LittleSheep
0251697951 Show robot on profile page 2025-08-23 17:32:49 +08:00
LittleSheep
178c12b893 Bot basis 2025-08-23 17:07:42 +08:00
LittleSheep
4beda9200e Add developer projects 2025-08-23 02:56:28 +08:00
LittleSheep
7dfe411053 Add more developer pages (wip) 2025-08-23 02:19:07 +08:00
LittleSheep
1232318a5d Add feed subscription (wip) 2025-08-23 01:32:58 +08:00
LittleSheep
56f41b6c0e 🔀 Merge pull request #173 from Texas0295/v3
🐛 linux/userinfo.dart: guard Firebase calls if Firebase is uninitialized
2025-08-23 01:28:54 +08:00
Texas0295
3ea717d25a 🐛 linux/userinfo.dart: guard Firebase calls if Firebase is uninitialized 2025-08-23 00:34:56 +08:00
LittleSheep
1fe4889460 Search for stickers 2025-08-22 23:31:31 +08:00
LittleSheep
cdf2722268 Stickers order by usage 2025-08-22 22:59:41 +08:00
LittleSheep
a127b5bace Social credits 2025-08-22 19:55:26 +08:00
LittleSheep
b2097cf044 🐛 Fix chat summary failed when lastMessage is null 2025-08-22 19:00:06 +08:00
LittleSheep
701f29748d Debug set access token 2025-08-22 18:59:40 +08:00
LittleSheep
9e40ed4600 Show leveling BonusMultiplier 2025-08-22 17:58:28 +08:00
LittleSheep
c90e6fe661 Experience records & refine leveling page 2025-08-22 17:53:07 +08:00
LittleSheep
569483300d Card shuffle 2025-08-22 16:20:13 +08:00
LittleSheep
bab602d98b Shuffle post 2025-08-22 01:41:25 +08:00
LittleSheep
b4f2bb803a ⬆️ Upgrade deps 2025-08-22 00:22:06 +08:00
LittleSheep
03bfed6f46 💄 Optimize cloud file info 2025-08-22 00:17:13 +08:00
LittleSheep
f98e5a0aec Post browse by categories, tags 2025-08-21 23:21:30 +08:00
LittleSheep
3d473e2fec 🐛 Replace the push with go to view posts in creator centre in order to fix #172 2025-08-21 20:09:49 +08:00
LittleSheep
0b6efa373a 💄 Optimize realm list 2025-08-21 18:49:42 +08:00
LittleSheep
9b60e96cde 💄 Optimize post detail error page 2025-08-21 02:25:13 +08:00
LittleSheep
81cd9b2082 🐛 Fix issues when saving image to gallery without ext name 2025-08-21 02:22:21 +08:00
LittleSheep
923d5d7514 👽 Update stickers api call 2025-08-20 14:26:37 +08:00
LittleSheep
7169aff841 🚀 Launch 3.2.0+127 2025-08-18 13:28:05 +08:00
LittleSheep
fac3efb50c 💄 Add status code to userinfo alert 2025-08-18 13:19:50 +08:00
LittleSheep
e809aadaea Userinfo failed to load alert 2025-08-18 13:08:57 +08:00
LittleSheep
f33b569221 🐛 Fix realm detail 2025-08-18 11:51:44 +08:00
LittleSheep
e5f2e2d146 🐛 Fix notification page cannot return 2025-08-18 11:43:12 +08:00
LittleSheep
11368d064f 🐛 Fix explore page 2025-08-18 11:42:42 +08:00
LittleSheep
246b163aec 🍱 Update notification icon 2025-08-18 02:36:29 +08:00
LittleSheep
10e0d2fe5f 🚀 Launch 3.2.0+126 2025-08-18 02:21:39 +08:00
LittleSheep
99e10cb612 Chat, realm member list with status 2025-08-18 02:01:13 +08:00
LittleSheep
1db6941431 💄 Optimize message styling 2025-08-17 23:13:59 +08:00
LittleSheep
8370da4fe3 Realm page redesgiened 2025-08-17 19:44:43 +08:00
LittleSheep
2bdf7029e9 💄 Optimize chat list tile 2025-08-17 13:51:02 +08:00
LittleSheep
86682a3a9a 💄 Optimize realm discovery list 2025-08-17 13:48:02 +08:00
LittleSheep
c3925e81b5 :drunk: Media offline error builder 2025-08-17 13:40:35 +08:00
LittleSheep
6f1f488490 💄 Limit realm card max description line 2025-08-17 13:36:02 +08:00
LittleSheep
31b2de2e46 💄 Update styling of realm list 2025-08-17 13:35:03 +08:00
LittleSheep
412dcfa62a 💄 Move the publisher invite icon to the list 2025-08-17 13:26:17 +08:00
LittleSheep
ffdc7e81ae 🐛 Make video being contained to prevent some issue 2025-08-17 13:15:55 +08:00
LittleSheep
1d3357803d 🐛 Fix post slug 2025-08-17 13:11:13 +08:00
LittleSheep
6c48aa2356 💄 Optimize attachment preview 2025-08-17 13:08:08 +08:00
LittleSheep
466e354679 💄 Optimize attachment in article 2025-08-17 13:02:52 +08:00
LittleSheep
5d4b896f70 Post slug 2025-08-17 12:47:00 +08:00
LittleSheep
a04dffdfe8 🐛 Fix missing sharePositionOrigin in share 2025-08-17 11:56:34 +08:00
LittleSheep
ff871943cf Show realm post indicator 2025-08-17 02:56:12 +08:00
LittleSheep
1a892ab227 Realm post, and post publisher is org is rounded rect 2025-08-17 02:37:45 +08:00
LittleSheep
af1b303211 Prevent user from setting empty links 2025-08-17 00:55:22 +08:00
LittleSheep
6fd702eba8 🐛 Fix profile update 2025-08-17 00:47:10 +08:00
LittleSheep
d220d43cd2 💄 Optimize chat room 2025-08-17 00:11:28 +08:00
LittleSheep
6892afb974 🔊 Add more logging and optimzation 2025-08-16 23:39:41 +08:00
LittleSheep
007b46b080 ♻️ Refactored the message repository logic 2025-08-16 23:07:21 +08:00
LittleSheep
67d130dc34 🔨 Sync the CMakeLists in linux to update date with the v2 modified version 2025-08-16 18:09:45 +08:00
LittleSheep
7e923c77fe 💄 Change explore screen wide mode breakpoint 2025-08-16 18:03:23 +08:00
LittleSheep
a593b52812 ♻️ Adjust the firebase analytics observer guard 2025-08-16 17:20:03 +08:00
LittleSheep
520dc80303 🔀 Merge pull request #169 from Texas0295/v3
🐛 linux: guard FirebaseAnalyticsObserver when Firebase is not initialized
2025-08-16 17:18:20 +08:00
LittleSheep
001897bbcd Notification indicator 2025-08-16 17:14:26 +08:00
Texas0295
bab29c23e3 🐛 linux: guard FirebaseAnalyticsObserver when Firebase is not initialized 2025-08-16 16:58:12 +08:00
LittleSheep
76b39f2df3 Mark all as read 2025-08-16 11:47:29 +08:00
LittleSheep
509b3e145b 🐛 Fix category selection render error 2025-08-16 02:39:00 +08:00
LittleSheep
2b80ebc2d0 🐛 Fix markdown image in chat close #167 2025-08-16 02:14:44 +08:00
LittleSheep
0ab908dd2a Auto collapse featured post if read 2025-08-16 02:02:06 +08:00
LittleSheep
6007467e7a 🐛 Fixes due to changes in backend 2025-08-15 03:33:20 +08:00
LittleSheep
3745157c42 💄 Optimize snackbars 2025-08-15 00:09:05 +08:00
LittleSheep
94481ec7bd 💫 Chnaged tab page animations 2025-08-14 14:21:57 +08:00
143 changed files with 12697 additions and 2985 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -0,0 +1,41 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="192dp"
android:height="192dp"
android:viewportWidth="192"
android:viewportHeight="192">
<path
android:pathData="M54,147h86"
android:strokeLineJoin="round"
android:strokeWidth="12"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
<path
android:pathData="M57,111s-2,-4.5 -2,-10m22,22s-4,7 -11,4m9,-22s-2,-4.5 -2,-10"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
<path
android:pathData="M54,147a32,32 0,0 1,-12 -61.67A39,39 0,0 1,81 46m59,101a30,30 0,0 0,29.93 -28"
android:strokeLineJoin="round"
android:strokeWidth="12"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
<path
android:pathData="M132,75m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"
android:strokeLineJoin="round"
android:strokeWidth="8"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
<path
android:pathData="M112.5,41.22C100.84,47.96 93,60.56 93,75c0,6.38 1.53,12.39 4.24,17.71m69.51,-35.42A38.84,38.84 0,0 1,171 75c0,14.43 -7.84,27.03 -19.49,33.78m-0.79,-43.32A20.9,20.9 0,0 1,153 75c0,7.77 -4.22,14.56 -10.49,18.19m-21,-36.38C115.22,60.44 111,67.23 111,75a20.9,20.9 0,0 0,2.28 9.53"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
</vector>

View File

@@ -334,6 +334,7 @@
"walletCreate": "Create a Wallet", "walletCreate": "Create a Wallet",
"settingsServerUrl": "Server URL", "settingsServerUrl": "Server URL",
"settingsApplied": "The settings has been applied.", "settingsApplied": "The settings has been applied.",
"settingsCustomFontsHelper": "Use comma to seprate.",
"notifications": "Notifications", "notifications": "Notifications",
"posts": "Posts", "posts": "Posts",
"settingsBackgroundImage": "Background Image", "settingsBackgroundImage": "Background Image",
@@ -642,6 +643,18 @@
"enrollDeveloperHint": "Enroll one of your publishers to become a developer.", "enrollDeveloperHint": "Enroll one of your publishers to become a developer.",
"noPublishersToEnroll": "You don't have any publishers that can be enrolled as a developer.", "noPublishersToEnroll": "You don't have any publishers that can be enrolled as a developer.",
"totalCustomApps": "Total Custom Apps", "totalCustomApps": "Total Custom Apps",
"projects": "Projects",
"noProjects": "No projects found.",
"deleteProject": "Delete Project",
"deleteProjectHint": "Are you sure you want to delete this project? This action cannot be undone.",
"createProject": "Create Project",
"editProject": "Edit Project",
"projectDetails": "Project Details",
"createBot": "Create Bot",
"bots": "Bots",
"noBots": "No bots yet.",
"deleteBotHint": "Are you sure you want to delete this bot? This action cannot be undone.",
"deleteBot": "Delete Bot",
"customApps": "Custom Apps", "customApps": "Custom Apps",
"noCustomApps": "No custom apps yet.", "noCustomApps": "No custom apps yet.",
"createCustomApp": "Create Custom App", "createCustomApp": "Create Custom App",
@@ -836,5 +849,82 @@
"pollAddOption": "Add option", "pollAddOption": "Add option",
"pollOptionLabel": "Option label", "pollOptionLabel": "Option label",
"pollLongTextAnswerPreview": "Long text answer (preview)", "pollLongTextAnswerPreview": "Long text answer (preview)",
"pollShortTextAnswerPreview": "Short text answer (preview)" "pollShortTextAnswerPreview": "Short text answer (preview)",
"messageJumpNotLoaded": "The referenced message was not loaded, unable to jump to it.",
"postUnlinkRealm": "No linked realm",
"postSlug": "Slug",
"postSlugHint": "The slug can be used to access your post via URL in the webpage, it should be publisher-wide unique.",
"attachmentOnDevice": "On-device",
"attachmentOnCloud": "On-cloud",
"attachments": "Attachments",
"publisherCollabInvitation": "Collabration invitations",
"publisherCollabInvitationCount": {
"zero": "No invitation",
"one": "{} available invitation",
"other": "{} available invitations"
},
"failedToLoadUserInfo": "Failed to load user info",
"failedToLoadUserInfoNetwork": "It seems be network issue, you can tap the button below to try again.",
"failedToLoadUserInfoUnauthorized": "It seems your session has been logged out or not available anymore, you can still try agian to fetch the user info if you want.",
"okay": "Okay",
"postDetail": "Post Detail",
"postCount": {
"zero": "No posts",
"one": "{} post",
"other": "{} posts"
},
"mimeType": "MIME Type",
"fileSize": "File Size",
"fileHash": "File Hash",
"exifData": "EXIF Data",
"postShuffle": "Shuffle Posts",
"leveling": "Leveling",
"levelingHistory": "Leveling History",
"stellarProgram": "Stellar Program",
"socialCredits": "Social Credits",
"credits": "Credits",
"socialCreditsDescription": "Social Credit is a way for Solar Network to evaluate users. It is calculated based on their behavior and interactions. With a base score of 100, higher scores indicate a user's credibility within the community. Scores change over time to reflect a user's recent behavior. Users with higher credit ratings enjoy more benefits, while users with lower credit ratings may have some functionality restricted.",
"socialCreditsLevelPoor": "Poor",
"socialCreditsLevelNormal": "Normal",
"socialCreditsLevelGood": "Good",
"socialCreditsLevelExcellent": "Excellent",
"orderByPopularity": "Sort by popularity",
"orderByReleaseDate": "Sort by release date",
"editBot": "Edit Bot",
"botAutomatedBy": "Automated by {}",
"botDetails": "Bot Details",
"overview": "Overview",
"keys": "Keys",
"botNotFound": "Bot not found.",
"newBotKey": "New Bot Key",
"newBotKeyHint": "Enter a name for your new key. The key will be shown only once.",
"revokeBotKey": "Revoke Bot Key",
"revokeBotKeyHint": "Are you sure you want to revoke this key? This action cannot be undone and any application using this key will stop working.",
"noBotKeys": "No bot keys yet.",
"revoke": "Revoke",
"keyName": "Key Name",
"newKeyGenerated": "New Key Generated",
"copyKeyHint": "Please copy this key and store it somewhere safe. You will not be able to see it again.",
"rotateKey": "Rotate Key",
"rotateBotKey": "Rotate Bot Key",
"rotateBotKeyHint": "Are you sure you want to rotate this key? The old key will become invalid immediately. This action cannot be undone.",
"webFeedArticleCount": {
"zero": "No articles",
"one": "{} article",
"other": "{} articles"
},
"webFeedSubscribed": "The feed has been subscribed",
"webFeedUnsubscribed": "The feed has been unsubscribed",
"appDetails": "App Details",
"secrets": "Secrets",
"appNotFound": "App not found.",
"secretCopied": "Secret copied to clipboard.",
"deleteSecret": "Delete Secret",
"deleteSecretHint": "Are you sure you want to delete this secret? This action cannot be undone.",
"generateSecret": "Generate New Secret",
"createdAt": "Created at {}",
"newSecretGenerated": "New Secret Generated",
"copySecretHint": "Please copy this secret and store it somewhere safe. You will not be able to see it again.",
"expiresIn": "Expires In (seconds)",
"isOidc": "OIDC Compliant"
} }

File diff suppressed because it is too large Load Diff

View File

@@ -811,5 +811,17 @@
"filesListAdditional": { "filesListAdditional": {
"one": "+{} 個文件被摺疊", "one": "+{} 個文件被摺疊",
"other": "+{} 個文件被摺疊" "other": "+{} 個文件被摺疊"
} },
"appDetails": "應用程式詳情",
"secrets": "密鑰",
"appNotFound": "找不到應用程式。",
"secretCopied": "密鑰已複製到剪貼簿。",
"deleteSecret": "刪除密鑰",
"deleteSecretHint": "您確定要刪除此密鑰嗎?此操作無法復原。",
"generateSecret": "產生新密鑰",
"createdAt": "建立於 {}",
"newSecretGenerated": "已產生新密鑰",
"copySecretHint": "請複製此密鑰並將其存放在安全的地方。您將無法再次看到它。",
"expiresIn": "過期時間(秒)",
"isOidc": "OIDC 相容"
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

View File

@@ -245,7 +245,7 @@ PODS:
- PromisesObjC (= 2.4.0) - PromisesObjC (= 2.4.0)
- receive_sharing_intent (1.8.1): - receive_sharing_intent (1.8.1):
- Flutter - Flutter
- record_ios (1.0.0): - record_ios (1.1.0):
- Flutter - Flutter
- SAMKeychain (1.5.3) - SAMKeychain (1.5.3)
- SDWebImage (5.21.1): - SDWebImage (5.21.1):
@@ -510,7 +510,7 @@ SPEC CHECKSUMS:
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00 receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00
record_ios: fee1c924aa4879b882ebca2b4bce6011bcfc3d8b record_ios: f75fa1d57f840012775c0e93a38a7f3ceea1a374
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
SDWebImage: f29024626962457f3470184232766516dee8dfea SDWebImage: f29024626962457f3470184232766516dee8dfea
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a

View File

@@ -1,484 +0,0 @@
import 'package:dio/dio.dart';
import 'package:island/database/drift_db.dart';
import 'package:island/database/message.dart';
import 'package:island/models/chat.dart';
import 'package:island/models/file.dart';
import 'package:island/services/file.dart';
import 'package:island/widgets/alert.dart';
import 'package:uuid/uuid.dart';
class MessageRepository {
final SnChatRoom room;
final SnChatMember identity;
final Dio _apiClient;
final AppDatabase _database;
final Map<String, LocalChatMessage> pendingMessages = {};
final Map<String, Map<int, double>> fileUploadProgress = {};
int? _totalCount;
MessageRepository(this.room, this.identity, this._apiClient, this._database);
Future<LocalChatMessage?> getLastMessages() async {
final dbMessages = await _database.getMessagesForRoom(
room.id,
offset: 0,
limit: 1,
);
if (dbMessages.isEmpty) {
return null;
}
return _database.companionToMessage(dbMessages.first);
}
Future<bool> syncMessages() async {
final lastMessage = await getLastMessages();
if (lastMessage == null) return false;
try {
final resp = await _apiClient.post(
'/sphere/chat/${room.id}/sync',
data: {
'last_sync_timestamp':
lastMessage.toRemoteMessage().updatedAt.millisecondsSinceEpoch,
},
);
final response = MessageSyncResponse.fromJson(resp.data);
for (final change in response.changes) {
switch (change.action) {
case MessageChangeAction.create:
await receiveMessage(change.message!);
break;
case MessageChangeAction.update:
await receiveMessageUpdate(change.message!);
break;
case MessageChangeAction.delete:
await receiveMessageDeletion(change.messageId.toString());
break;
}
}
} catch (err) {
showErrorAlert(err);
}
return true;
}
Future<List<LocalChatMessage>> listMessages({
int offset = 0,
int take = 20,
bool synced = false,
}) async {
try {
// For initial load, fetch latest messages in the background to sync.
if (offset == 0 && !synced) {
// Not awaiting this is intentional, for a quicker UI response.
// The UI should rely on a stream from the database to get updates.
_fetchAndCacheMessages(room.id, offset: 0, take: take).catchError((_) {
// Best effort, errors will be handled by later fetches.
return <LocalChatMessage>[];
});
}
final localMessages = await _getCachedMessages(
room.id,
offset: offset,
take: take,
);
// If local cache has messages, return them. This is the common case for scrolling up.
if (localMessages.isNotEmpty) {
return localMessages;
}
// If local cache is empty, we've probably reached the end of cached history.
// Fetch from remote. This will also be hit on first load if cache is empty.
return await _fetchAndCacheMessages(room.id, offset: offset, take: take);
} catch (e) {
// Final fallback to cache in case of network errors during fetch.
final localMessages = await _getCachedMessages(
room.id,
offset: offset,
take: take,
);
if (localMessages.isNotEmpty) {
return localMessages;
}
rethrow;
}
}
Future<List<LocalChatMessage>> _getCachedMessages(
String roomId, {
int offset = 0,
int take = 20,
}) async {
// Get messages from local database
final dbMessages = await _database.getMessagesForRoom(
roomId,
offset: offset,
limit: take,
);
final dbLocalMessages =
dbMessages.map(_database.companionToMessage).toList();
// Combine with pending messages for the first page
if (offset == 0) {
final pendingForRoom =
pendingMessages.values.where((msg) => msg.roomId == roomId).toList();
final allMessages = [...pendingForRoom, ...dbLocalMessages];
allMessages.sort((a, b) => b.createdAt.compareTo(a.createdAt));
// Remove duplicates by ID, preserving the order
final uniqueMessages = <LocalChatMessage>[];
final seenIds = <String>{};
for (final message in allMessages) {
if (seenIds.add(message.id)) {
uniqueMessages.add(message);
}
}
return uniqueMessages;
}
return dbLocalMessages;
}
Future<List<LocalChatMessage>> _fetchAndCacheMessages(
String roomId, {
int offset = 0,
int take = 20,
}) async {
// Use cached total count if available, otherwise fetch it
if (_totalCount == null) {
final response = await _apiClient.get(
'/sphere/chat/$roomId/messages',
queryParameters: {'offset': 0, 'take': 1},
);
_totalCount = int.parse(response.headers['x-total']?.firstOrNull ?? '0');
}
if (offset >= _totalCount!) {
return [];
}
final response = await _apiClient.get(
'/sphere/chat/$roomId/messages',
queryParameters: {'offset': offset, 'take': take},
);
final List<dynamic> data = response.data;
// Update total count from response headers
_totalCount = int.parse(response.headers['x-total']?.firstOrNull ?? '0');
final messages =
data.map((json) {
final remoteMessage = SnChatMessage.fromJson(json);
return LocalChatMessage.fromRemoteMessage(
remoteMessage,
MessageStatus.sent,
);
}).toList();
for (final message in messages) {
await _database.saveMessage(_database.messageToCompanion(message));
if (message.nonce != null) {
pendingMessages.removeWhere(
(_, pendingMsg) => pendingMsg.nonce == message.nonce,
);
}
}
return messages;
}
Future<LocalChatMessage> sendMessage(
String token,
String baseUrl,
String roomId,
String content,
String nonce, {
required List<UniversalFile> attachments,
Map<String, dynamic>? meta,
SnChatMessage? replyingTo,
SnChatMessage? forwardingTo,
SnChatMessage? editingTo,
Function(LocalChatMessage)? onPending,
Function(String, Map<int, double>)? onProgress,
}) async {
// Generate a unique nonce for this message
final nonce = const Uuid().v4();
// Create a local message with pending status
final mockMessage = SnChatMessage(
id: 'pending_$nonce',
chatRoomId: roomId,
senderId: identity.id,
content: content,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
nonce: nonce,
sender: identity,
);
final localMessage = LocalChatMessage.fromRemoteMessage(
mockMessage,
MessageStatus.pending,
);
// Store in memory and database
pendingMessages[localMessage.id] = localMessage;
fileUploadProgress[localMessage.id] = {};
await _database.saveMessage(_database.messageToCompanion(localMessage));
onPending?.call(localMessage);
try {
var cloudAttachments = List.empty(growable: true);
// Upload files
for (var idx = 0; idx < attachments.length; idx++) {
final cloudFile =
await putMediaToCloud(
fileData: attachments[idx],
atk: token,
baseUrl: baseUrl,
filename: attachments[idx].data.name ?? 'Post media',
mimetype:
attachments[idx].data.mimeType ??
switch (attachments[idx].type) {
UniversalFileType.image => 'image/unknown',
UniversalFileType.video => 'video/unknown',
UniversalFileType.audio => 'audio/unknown',
UniversalFileType.file => 'application/octet-stream',
},
onProgress: (progress, _) {
fileUploadProgress[localMessage.id]?[idx] = progress;
onProgress?.call(
localMessage.id,
fileUploadProgress[localMessage.id] ?? {},
);
},
).future;
if (cloudFile == null) {
throw ArgumentError('Failed to upload the file...');
}
cloudAttachments.add(cloudFile);
}
// Send to server
final response = await _apiClient.request(
editingTo == null
? '/sphere/chat/$roomId/messages'
: '/sphere/chat/$roomId/messages/${editingTo.id}',
data: {
'content': content,
'attachments_id': cloudAttachments.map((e) => e.id).toList(),
'replied_message_id': replyingTo?.id,
'forwarded_message_id': forwardingTo?.id,
'meta': meta,
'nonce': nonce,
},
options: Options(method: editingTo == null ? 'POST' : 'PATCH'),
);
// Update with server response
final remoteMessage = SnChatMessage.fromJson(response.data);
final updatedMessage = LocalChatMessage.fromRemoteMessage(
remoteMessage,
MessageStatus.sent,
);
// Remove from pending and update in database
pendingMessages.remove(localMessage.id);
await _database.deleteMessage(localMessage.id);
await _database.saveMessage(_database.messageToCompanion(updatedMessage));
return updatedMessage;
} catch (e) {
// Update status to failed
localMessage.status = MessageStatus.failed;
pendingMessages[localMessage.id] = localMessage;
await _database.updateMessageStatus(
localMessage.id,
MessageStatus.failed,
);
rethrow;
}
}
Future<LocalChatMessage> retryMessage(String pendingMessageId) async {
final message = await getMessageById(pendingMessageId);
if (message == null) {
throw Exception('Message not found');
}
// Update status back to pending
message.status = MessageStatus.pending;
pendingMessages[pendingMessageId] = message;
await _database.updateMessageStatus(
pendingMessageId,
MessageStatus.pending,
);
try {
// Send to server
var remoteMessage = message.toRemoteMessage();
final response = await _apiClient.post(
'/sphere/chat/${message.roomId}/messages',
data: {
'content': remoteMessage.content,
'attachments_id': remoteMessage.attachments,
'meta': remoteMessage.meta,
'nonce': message.nonce,
},
);
// Update with server response
remoteMessage = SnChatMessage.fromJson(response.data);
final updatedMessage = LocalChatMessage.fromRemoteMessage(
remoteMessage,
MessageStatus.sent,
);
// Remove from pending and update in database
pendingMessages.remove(pendingMessageId);
await _database.deleteMessage(pendingMessageId);
await _database.saveMessage(_database.messageToCompanion(updatedMessage));
return updatedMessage;
} catch (e) {
// Update status to failed
message.status = MessageStatus.failed;
pendingMessages[pendingMessageId] = message;
await _database.updateMessageStatus(
pendingMessageId,
MessageStatus.failed,
);
rethrow;
}
}
Future<LocalChatMessage> receiveMessage(SnChatMessage remoteMessage) async {
final localMessage = LocalChatMessage.fromRemoteMessage(
remoteMessage,
MessageStatus.sent,
);
if (remoteMessage.nonce != null) {
pendingMessages.removeWhere(
(_, pendingMsg) => pendingMsg.nonce == remoteMessage.nonce,
);
}
await _database.saveMessage(_database.messageToCompanion(localMessage));
return localMessage;
}
Future<LocalChatMessage> receiveMessageUpdate(
SnChatMessage remoteMessage,
) async {
final localMessage = LocalChatMessage.fromRemoteMessage(
remoteMessage,
MessageStatus.sent,
);
await _database.updateMessage(_database.messageToCompanion(localMessage));
return localMessage;
}
Future<void> receiveMessageDeletion(String messageId) async {
// Remove from pending messages if exists
pendingMessages.remove(messageId);
// Delete from local database
await _database.deleteMessage(messageId);
}
Future<LocalChatMessage> updateMessage(
String messageId,
String content, {
List<SnCloudFile>? attachments,
Map<String, dynamic>? meta,
}) async {
final message = pendingMessages[messageId];
if (message != null) {
// Update pending message
final rmMessage = message.toRemoteMessage();
final updatedRemoteMessage = rmMessage.copyWith(
content: content,
meta: meta ?? rmMessage.meta,
);
final updatedLocalMessage = LocalChatMessage.fromRemoteMessage(
updatedRemoteMessage,
MessageStatus.pending,
);
pendingMessages[messageId] = updatedLocalMessage;
await _database.updateMessage(
_database.messageToCompanion(updatedLocalMessage),
);
return message;
}
try {
// Update on server
final response = await _apiClient.put(
'/sphere/chat/${room.id}/messages/$messageId',
data: {'content': content, 'attachments': attachments, 'meta': meta},
);
// Update local copy
final remoteMessage = SnChatMessage.fromJson(response.data);
final updatedMessage = LocalChatMessage.fromRemoteMessage(
remoteMessage,
MessageStatus.sent,
);
await _database.updateMessage(
_database.messageToCompanion(updatedMessage),
);
return updatedMessage;
} catch (e) {
rethrow;
}
}
Future<void> deleteMessage(String messageId) async {
try {
await _apiClient.delete('/sphere/chat/${room.id}/messages/$messageId');
pendingMessages.remove(messageId);
await _database.deleteMessage(messageId);
} catch (e) {
rethrow;
}
}
Future<LocalChatMessage?> getMessageById(String messageId) async {
try {
// Attempt to get the message from the local database
final localMessage =
await (_database.select(_database.chatMessages)
..where((tbl) => tbl.id.equals(messageId))).getSingleOrNull();
if (localMessage != null) {
return _database.companionToMessage(localMessage);
}
// If not found locally, fetch from the server
final response = await _apiClient.get(
'/sphere/chat/${room.id}/messages/$messageId',
);
final remoteMessage = SnChatMessage.fromJson(response.data);
final message = LocalChatMessage.fromRemoteMessage(
remoteMessage,
MessageStatus.sent,
);
// Save the fetched message to the local database
await _database.saveMessage(_database.messageToCompanion(message));
return message;
} catch (e) {
if (e is DioException) return null;
// Handle errors
rethrow;
}
}
}

View File

@@ -14,6 +14,7 @@ sealed class SnAccount with _$SnAccount {
required String nick, required String nick,
required String language, required String language,
required bool isSuperuser, required bool isSuperuser,
required String? automatedId,
required SnAccountProfile profile, required SnAccountProfile profile,
required SnWalletSubscriptionRef? perkSubscription, required SnWalletSubscriptionRef? perkSubscription,
@Default([]) List<SnAccountBadge> badges, @Default([]) List<SnAccountBadge> badges,
@@ -208,3 +209,37 @@ sealed class SnAuthDeviceWithChallenge with _$SnAuthDeviceWithChallenge {
factory SnAuthDeviceWithChallenge.fromJson(Map<String, dynamic> json) => factory SnAuthDeviceWithChallenge.fromJson(Map<String, dynamic> json) =>
_$SnAuthDeviceWithChallengeFromJson(json); _$SnAuthDeviceWithChallengeFromJson(json);
} }
@freezed
sealed class SnExperienceRecord with _$SnExperienceRecord {
const factory SnExperienceRecord({
required String id,
required int delta,
required String reasonType,
required String reason,
@Default(1.0) double? bonusMultiplier,
required DateTime createdAt,
required DateTime updatedAt,
required DateTime? deletedAt,
}) = _SnExperienceRecord;
factory SnExperienceRecord.fromJson(Map<String, dynamic> json) =>
_$SnExperienceRecordFromJson(json);
}
@freezed
sealed class SnSocialCreditRecord with _$SnSocialCreditRecord {
const factory SnSocialCreditRecord({
required String id,
required double delta,
required String reasonType,
required String reason,
required DateTime? expiredAt,
required DateTime createdAt,
required DateTime updatedAt,
required DateTime? deletedAt,
}) = _SnSocialCreditRecord;
factory SnSocialCreditRecord.fromJson(Map<String, dynamic> json) =>
_$SnSocialCreditRecordFromJson(json);
}

View File

@@ -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; bool get isSuperuser; 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; 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;
/// 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.isSuperuser, isSuperuser) || other.isSuperuser == isSuperuser)&&(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.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));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,id,name,nick,language,isSuperuser,profile,perkSubscription,const DeepCollectionEquality().hash(badges),createdAt,updatedAt,deletedAt); int get hashCode => Object.hash(runtimeType,id,name,nick,language,isSuperuser,automatedId,profile,perkSubscription,const DeepCollectionEquality().hash(badges),createdAt,updatedAt,deletedAt);
@override @override
String toString() { String toString() {
return 'SnAccount(id: $id, name: $name, nick: $nick, language: $language, isSuperuser: $isSuperuser, profile: $profile, perkSubscription: $perkSubscription, badges: $badges, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; return 'SnAccount(id: $id, name: $name, nick: $nick, language: $language, isSuperuser: $isSuperuser, automatedId: $automatedId, profile: $profile, perkSubscription: $perkSubscription, badges: $badges, 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, bool isSuperuser, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt String id, String name, String nick, String language, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
}); });
@@ -65,14 +65,15 @@ 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? isSuperuser = null,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? isSuperuser = null,Object? automatedId = freezed,Object? profile = null,Object? perkSubscription = freezed,Object? badges = 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
as String,nick: null == nick ? _self.nick : nick // ignore: cast_nullable_to_non_nullable as String,nick: null == nick ? _self.nick : nick // ignore: cast_nullable_to_non_nullable
as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable
as String,isSuperuser: null == isSuperuser ? _self.isSuperuser : isSuperuser // ignore: cast_nullable_to_non_nullable as String,isSuperuser: null == isSuperuser ? _self.isSuperuser : isSuperuser // ignore: cast_nullable_to_non_nullable
as bool,profile: null == profile ? _self.profile : profile // ignore: cast_nullable_to_non_nullable as bool,automatedId: freezed == automatedId ? _self.automatedId : automatedId // 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>,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
@@ -181,10 +182,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String name, String nick, String language, bool isSuperuser, 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, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, 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.isSuperuser,_that.profile,_that.perkSubscription,_that.badges,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return $default(_that.id,_that.name,_that.nick,_that.language,_that.isSuperuser,_that.automatedId,_that.profile,_that.perkSubscription,_that.badges,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
return orElse(); return orElse();
} }
@@ -202,10 +203,10 @@ return $default(_that.id,_that.name,_that.nick,_that.language,_that.isSuperuser,
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String name, String nick, String language, bool isSuperuser, 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, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, 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.isSuperuser,_that.profile,_that.perkSubscription,_that.badges,_that.createdAt,_that.updatedAt,_that.deletedAt);} return $default(_that.id,_that.name,_that.nick,_that.language,_that.isSuperuser,_that.automatedId,_that.profile,_that.perkSubscription,_that.badges,_that.createdAt,_that.updatedAt,_that.deletedAt);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
/// ///
@@ -219,10 +220,10 @@ return $default(_that.id,_that.name,_that.nick,_that.language,_that.isSuperuser,
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String name, String nick, String language, bool isSuperuser, 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, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, 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.isSuperuser,_that.profile,_that.perkSubscription,_that.badges,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return $default(_that.id,_that.name,_that.nick,_that.language,_that.isSuperuser,_that.automatedId,_that.profile,_that.perkSubscription,_that.badges,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
return null; return null;
} }
@@ -234,7 +235,7 @@ return $default(_that.id,_that.name,_that.nick,_that.language,_that.isSuperuser,
@JsonSerializable() @JsonSerializable()
class _SnAccount implements SnAccount { class _SnAccount implements SnAccount {
const _SnAccount({required this.id, required this.name, required this.nick, required this.language, required this.isSuperuser, 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, 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;
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;
@@ -242,6 +243,7 @@ class _SnAccount implements SnAccount {
@override final String nick; @override final String nick;
@override final String language; @override final String language;
@override final bool isSuperuser; @override final bool isSuperuser;
@override final String? automatedId;
@override final SnAccountProfile profile; @override final SnAccountProfile profile;
@override final SnWalletSubscriptionRef? perkSubscription; @override final SnWalletSubscriptionRef? perkSubscription;
final List<SnAccountBadge> _badges; final List<SnAccountBadge> _badges;
@@ -268,16 +270,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.isSuperuser, isSuperuser) || other.isSuperuser == isSuperuser)&&(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.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));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,id,name,nick,language,isSuperuser,profile,perkSubscription,const DeepCollectionEquality().hash(_badges),createdAt,updatedAt,deletedAt); int get hashCode => Object.hash(runtimeType,id,name,nick,language,isSuperuser,automatedId,profile,perkSubscription,const DeepCollectionEquality().hash(_badges),createdAt,updatedAt,deletedAt);
@override @override
String toString() { String toString() {
return 'SnAccount(id: $id, name: $name, nick: $nick, language: $language, isSuperuser: $isSuperuser, profile: $profile, perkSubscription: $perkSubscription, badges: $badges, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; return 'SnAccount(id: $id, name: $name, nick: $nick, language: $language, isSuperuser: $isSuperuser, automatedId: $automatedId, profile: $profile, perkSubscription: $perkSubscription, badges: $badges, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
} }
@@ -288,7 +290,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, bool isSuperuser, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt String id, String name, String nick, String language, bool isSuperuser, String? automatedId, SnAccountProfile profile, SnWalletSubscriptionRef? perkSubscription, List<SnAccountBadge> badges, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
}); });
@@ -305,14 +307,15 @@ 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? isSuperuser = null,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? isSuperuser = null,Object? automatedId = freezed,Object? profile = null,Object? perkSubscription = freezed,Object? badges = 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
as String,nick: null == nick ? _self.nick : nick // ignore: cast_nullable_to_non_nullable as String,nick: null == nick ? _self.nick : nick // ignore: cast_nullable_to_non_nullable
as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable
as String,isSuperuser: null == isSuperuser ? _self.isSuperuser : isSuperuser // ignore: cast_nullable_to_non_nullable as String,isSuperuser: null == isSuperuser ? _self.isSuperuser : isSuperuser // ignore: cast_nullable_to_non_nullable
as bool,profile: null == profile ? _self.profile : profile // ignore: cast_nullable_to_non_nullable as bool,automatedId: freezed == automatedId ? _self.automatedId : automatedId // 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>,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
@@ -3018,6 +3021,562 @@ as bool,
} }
}
/// @nodoc
mixin _$SnExperienceRecord {
String get id; int get delta; String get reasonType; String get reason; double? get bonusMultiplier; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
/// Create a copy of SnExperienceRecord
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$SnExperienceRecordCopyWith<SnExperienceRecord> get copyWith => _$SnExperienceRecordCopyWithImpl<SnExperienceRecord>(this as SnExperienceRecord, _$identity);
/// Serializes this SnExperienceRecord to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnExperienceRecord&&(identical(other.id, id) || other.id == id)&&(identical(other.delta, delta) || other.delta == delta)&&(identical(other.reasonType, reasonType) || other.reasonType == reasonType)&&(identical(other.reason, reason) || other.reason == reason)&&(identical(other.bonusMultiplier, bonusMultiplier) || other.bonusMultiplier == bonusMultiplier)&&(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)
@override
int get hashCode => Object.hash(runtimeType,id,delta,reasonType,reason,bonusMultiplier,createdAt,updatedAt,deletedAt);
@override
String toString() {
return 'SnExperienceRecord(id: $id, delta: $delta, reasonType: $reasonType, reason: $reason, bonusMultiplier: $bonusMultiplier, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
}
}
/// @nodoc
abstract mixin class $SnExperienceRecordCopyWith<$Res> {
factory $SnExperienceRecordCopyWith(SnExperienceRecord value, $Res Function(SnExperienceRecord) _then) = _$SnExperienceRecordCopyWithImpl;
@useResult
$Res call({
String id, int delta, String reasonType, String reason, double? bonusMultiplier, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
});
}
/// @nodoc
class _$SnExperienceRecordCopyWithImpl<$Res>
implements $SnExperienceRecordCopyWith<$Res> {
_$SnExperienceRecordCopyWithImpl(this._self, this._then);
final SnExperienceRecord _self;
final $Res Function(SnExperienceRecord) _then;
/// Create a copy of SnExperienceRecord
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? delta = null,Object? reasonType = null,Object? reason = null,Object? bonusMultiplier = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,delta: null == delta ? _self.delta : delta // ignore: cast_nullable_to_non_nullable
as int,reasonType: null == reasonType ? _self.reasonType : reasonType // ignore: cast_nullable_to_non_nullable
as String,reason: null == reason ? _self.reason : reason // ignore: cast_nullable_to_non_nullable
as String,bonusMultiplier: freezed == bonusMultiplier ? _self.bonusMultiplier : bonusMultiplier // ignore: cast_nullable_to_non_nullable
as double?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
));
}
}
/// Adds pattern-matching-related methods to [SnExperienceRecord].
extension SnExperienceRecordPatterns on SnExperienceRecord {
/// 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( _SnExperienceRecord value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _SnExperienceRecord() 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( _SnExperienceRecord value) $default,){
final _that = this;
switch (_that) {
case _SnExperienceRecord():
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( _SnExperienceRecord value)? $default,){
final _that = this;
switch (_that) {
case _SnExperienceRecord() 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 id, int delta, String reasonType, String reason, double? bonusMultiplier, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SnExperienceRecord() when $default != null:
return $default(_that.id,_that.delta,_that.reasonType,_that.reason,_that.bonusMultiplier,_that.createdAt,_that.updatedAt,_that.deletedAt);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 id, int delta, String reasonType, String reason, double? bonusMultiplier, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
switch (_that) {
case _SnExperienceRecord():
return $default(_that.id,_that.delta,_that.reasonType,_that.reason,_that.bonusMultiplier,_that.createdAt,_that.updatedAt,_that.deletedAt);}
}
/// 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 id, int delta, String reasonType, String reason, double? bonusMultiplier, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
switch (_that) {
case _SnExperienceRecord() when $default != null:
return $default(_that.id,_that.delta,_that.reasonType,_that.reason,_that.bonusMultiplier,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _SnExperienceRecord implements SnExperienceRecord {
const _SnExperienceRecord({required this.id, required this.delta, required this.reasonType, required this.reason, this.bonusMultiplier = 1.0, required this.createdAt, required this.updatedAt, required this.deletedAt});
factory _SnExperienceRecord.fromJson(Map<String, dynamic> json) => _$SnExperienceRecordFromJson(json);
@override final String id;
@override final int delta;
@override final String reasonType;
@override final String reason;
@override@JsonKey() final double? bonusMultiplier;
@override final DateTime createdAt;
@override final DateTime updatedAt;
@override final DateTime? deletedAt;
/// Create a copy of SnExperienceRecord
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SnExperienceRecordCopyWith<_SnExperienceRecord> get copyWith => __$SnExperienceRecordCopyWithImpl<_SnExperienceRecord>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$SnExperienceRecordToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnExperienceRecord&&(identical(other.id, id) || other.id == id)&&(identical(other.delta, delta) || other.delta == delta)&&(identical(other.reasonType, reasonType) || other.reasonType == reasonType)&&(identical(other.reason, reason) || other.reason == reason)&&(identical(other.bonusMultiplier, bonusMultiplier) || other.bonusMultiplier == bonusMultiplier)&&(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)
@override
int get hashCode => Object.hash(runtimeType,id,delta,reasonType,reason,bonusMultiplier,createdAt,updatedAt,deletedAt);
@override
String toString() {
return 'SnExperienceRecord(id: $id, delta: $delta, reasonType: $reasonType, reason: $reason, bonusMultiplier: $bonusMultiplier, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
}
}
/// @nodoc
abstract mixin class _$SnExperienceRecordCopyWith<$Res> implements $SnExperienceRecordCopyWith<$Res> {
factory _$SnExperienceRecordCopyWith(_SnExperienceRecord value, $Res Function(_SnExperienceRecord) _then) = __$SnExperienceRecordCopyWithImpl;
@override @useResult
$Res call({
String id, int delta, String reasonType, String reason, double? bonusMultiplier, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
});
}
/// @nodoc
class __$SnExperienceRecordCopyWithImpl<$Res>
implements _$SnExperienceRecordCopyWith<$Res> {
__$SnExperienceRecordCopyWithImpl(this._self, this._then);
final _SnExperienceRecord _self;
final $Res Function(_SnExperienceRecord) _then;
/// Create a copy of SnExperienceRecord
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? delta = null,Object? reasonType = null,Object? reason = null,Object? bonusMultiplier = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
return _then(_SnExperienceRecord(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,delta: null == delta ? _self.delta : delta // ignore: cast_nullable_to_non_nullable
as int,reasonType: null == reasonType ? _self.reasonType : reasonType // ignore: cast_nullable_to_non_nullable
as String,reason: null == reason ? _self.reason : reason // ignore: cast_nullable_to_non_nullable
as String,bonusMultiplier: freezed == bonusMultiplier ? _self.bonusMultiplier : bonusMultiplier // ignore: cast_nullable_to_non_nullable
as double?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
));
}
}
/// @nodoc
mixin _$SnSocialCreditRecord {
String get id; double get delta; String get reasonType; String get reason; DateTime? get expiredAt; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
/// Create a copy of SnSocialCreditRecord
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$SnSocialCreditRecordCopyWith<SnSocialCreditRecord> get copyWith => _$SnSocialCreditRecordCopyWithImpl<SnSocialCreditRecord>(this as SnSocialCreditRecord, _$identity);
/// Serializes this SnSocialCreditRecord to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnSocialCreditRecord&&(identical(other.id, id) || other.id == id)&&(identical(other.delta, delta) || other.delta == delta)&&(identical(other.reasonType, reasonType) || other.reasonType == reasonType)&&(identical(other.reason, reason) || other.reason == reason)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&(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)
@override
int get hashCode => Object.hash(runtimeType,id,delta,reasonType,reason,expiredAt,createdAt,updatedAt,deletedAt);
@override
String toString() {
return 'SnSocialCreditRecord(id: $id, delta: $delta, reasonType: $reasonType, reason: $reason, expiredAt: $expiredAt, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
}
}
/// @nodoc
abstract mixin class $SnSocialCreditRecordCopyWith<$Res> {
factory $SnSocialCreditRecordCopyWith(SnSocialCreditRecord value, $Res Function(SnSocialCreditRecord) _then) = _$SnSocialCreditRecordCopyWithImpl;
@useResult
$Res call({
String id, double delta, String reasonType, String reason, DateTime? expiredAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
});
}
/// @nodoc
class _$SnSocialCreditRecordCopyWithImpl<$Res>
implements $SnSocialCreditRecordCopyWith<$Res> {
_$SnSocialCreditRecordCopyWithImpl(this._self, this._then);
final SnSocialCreditRecord _self;
final $Res Function(SnSocialCreditRecord) _then;
/// Create a copy of SnSocialCreditRecord
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? delta = null,Object? reasonType = null,Object? reason = null,Object? expiredAt = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,delta: null == delta ? _self.delta : delta // ignore: cast_nullable_to_non_nullable
as double,reasonType: null == reasonType ? _self.reasonType : reasonType // ignore: cast_nullable_to_non_nullable
as String,reason: null == reason ? _self.reason : reason // ignore: cast_nullable_to_non_nullable
as String,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
as DateTime?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
));
}
}
/// Adds pattern-matching-related methods to [SnSocialCreditRecord].
extension SnSocialCreditRecordPatterns on SnSocialCreditRecord {
/// 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( _SnSocialCreditRecord value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _SnSocialCreditRecord() 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( _SnSocialCreditRecord value) $default,){
final _that = this;
switch (_that) {
case _SnSocialCreditRecord():
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( _SnSocialCreditRecord value)? $default,){
final _that = this;
switch (_that) {
case _SnSocialCreditRecord() 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 id, double delta, String reasonType, String reason, DateTime? expiredAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SnSocialCreditRecord() when $default != null:
return $default(_that.id,_that.delta,_that.reasonType,_that.reason,_that.expiredAt,_that.createdAt,_that.updatedAt,_that.deletedAt);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 id, double delta, String reasonType, String reason, DateTime? expiredAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
switch (_that) {
case _SnSocialCreditRecord():
return $default(_that.id,_that.delta,_that.reasonType,_that.reason,_that.expiredAt,_that.createdAt,_that.updatedAt,_that.deletedAt);}
}
/// 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 id, double delta, String reasonType, String reason, DateTime? expiredAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
switch (_that) {
case _SnSocialCreditRecord() when $default != null:
return $default(_that.id,_that.delta,_that.reasonType,_that.reason,_that.expiredAt,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _SnSocialCreditRecord implements SnSocialCreditRecord {
const _SnSocialCreditRecord({required this.id, required this.delta, required this.reasonType, required this.reason, required this.expiredAt, required this.createdAt, required this.updatedAt, required this.deletedAt});
factory _SnSocialCreditRecord.fromJson(Map<String, dynamic> json) => _$SnSocialCreditRecordFromJson(json);
@override final String id;
@override final double delta;
@override final String reasonType;
@override final String reason;
@override final DateTime? expiredAt;
@override final DateTime createdAt;
@override final DateTime updatedAt;
@override final DateTime? deletedAt;
/// Create a copy of SnSocialCreditRecord
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SnSocialCreditRecordCopyWith<_SnSocialCreditRecord> get copyWith => __$SnSocialCreditRecordCopyWithImpl<_SnSocialCreditRecord>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$SnSocialCreditRecordToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnSocialCreditRecord&&(identical(other.id, id) || other.id == id)&&(identical(other.delta, delta) || other.delta == delta)&&(identical(other.reasonType, reasonType) || other.reasonType == reasonType)&&(identical(other.reason, reason) || other.reason == reason)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&(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)
@override
int get hashCode => Object.hash(runtimeType,id,delta,reasonType,reason,expiredAt,createdAt,updatedAt,deletedAt);
@override
String toString() {
return 'SnSocialCreditRecord(id: $id, delta: $delta, reasonType: $reasonType, reason: $reason, expiredAt: $expiredAt, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
}
}
/// @nodoc
abstract mixin class _$SnSocialCreditRecordCopyWith<$Res> implements $SnSocialCreditRecordCopyWith<$Res> {
factory _$SnSocialCreditRecordCopyWith(_SnSocialCreditRecord value, $Res Function(_SnSocialCreditRecord) _then) = __$SnSocialCreditRecordCopyWithImpl;
@override @useResult
$Res call({
String id, double delta, String reasonType, String reason, DateTime? expiredAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
});
}
/// @nodoc
class __$SnSocialCreditRecordCopyWithImpl<$Res>
implements _$SnSocialCreditRecordCopyWith<$Res> {
__$SnSocialCreditRecordCopyWithImpl(this._self, this._then);
final _SnSocialCreditRecord _self;
final $Res Function(_SnSocialCreditRecord) _then;
/// Create a copy of SnSocialCreditRecord
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? delta = null,Object? reasonType = null,Object? reason = null,Object? expiredAt = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
return _then(_SnSocialCreditRecord(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,delta: null == delta ? _self.delta : delta // ignore: cast_nullable_to_non_nullable
as double,reasonType: null == reasonType ? _self.reasonType : reasonType // ignore: cast_nullable_to_non_nullable
as String,reason: null == reason ? _self.reason : reason // ignore: cast_nullable_to_non_nullable
as String,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
as DateTime?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
));
}
} }
// dart format on // dart format on

View File

@@ -12,6 +12,7 @@ _SnAccount _$SnAccountFromJson(Map<String, dynamic> json) => _SnAccount(
nick: json['nick'] as String, nick: json['nick'] as String,
language: json['language'] as String, language: json['language'] as String,
isSuperuser: json['is_superuser'] as bool, isSuperuser: json['is_superuser'] as bool,
automatedId: json['automated_id'] as String?,
profile: SnAccountProfile.fromJson(json['profile'] as Map<String, dynamic>), profile: SnAccountProfile.fromJson(json['profile'] as Map<String, dynamic>),
perkSubscription: perkSubscription:
json['perk_subscription'] == null json['perk_subscription'] == null
@@ -39,6 +40,7 @@ Map<String, dynamic> _$SnAccountToJson(_SnAccount instance) =>
'nick': instance.nick, 'nick': instance.nick,
'language': instance.language, 'language': instance.language,
'is_superuser': instance.isSuperuser, 'is_superuser': instance.isSuperuser,
'automated_id': instance.automatedId,
'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(),
@@ -348,3 +350,62 @@ Map<String, dynamic> _$SnAuthDeviceWithChallengeeToJson(
'challenges': instance.challenges.map((e) => e.toJson()).toList(), 'challenges': instance.challenges.map((e) => e.toJson()).toList(),
'is_current': instance.isCurrent, 'is_current': instance.isCurrent,
}; };
_SnExperienceRecord _$SnExperienceRecordFromJson(Map<String, dynamic> json) =>
_SnExperienceRecord(
id: json['id'] as String,
delta: (json['delta'] as num).toInt(),
reasonType: json['reason_type'] as String,
reason: json['reason'] as String,
bonusMultiplier: (json['bonus_multiplier'] as num?)?.toDouble() ?? 1.0,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt:
json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
);
Map<String, dynamic> _$SnExperienceRecordToJson(_SnExperienceRecord instance) =>
<String, dynamic>{
'id': instance.id,
'delta': instance.delta,
'reason_type': instance.reasonType,
'reason': instance.reason,
'bonus_multiplier': instance.bonusMultiplier,
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
};
_SnSocialCreditRecord _$SnSocialCreditRecordFromJson(
Map<String, dynamic> json,
) => _SnSocialCreditRecord(
id: json['id'] as String,
delta: (json['delta'] as num).toDouble(),
reasonType: json['reason_type'] as String,
reason: json['reason'] as String,
expiredAt:
json['expired_at'] == null
? null
: DateTime.parse(json['expired_at'] as String),
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt:
json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
);
Map<String, dynamic> _$SnSocialCreditRecordToJson(
_SnSocialCreditRecord instance,
) => <String, dynamic>{
'id': instance.id,
'delta': instance.delta,
'reason_type': instance.reasonType,
'reason': instance.reason,
'expired_at': instance.expiredAt?.toIso8601String(),
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
};

63
lib/models/bot.dart Normal file
View File

@@ -0,0 +1,63 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:island/models/account.dart';
import 'package:island/models/developer.dart';
part 'bot.freezed.dart';
part 'bot.g.dart';
@freezed
sealed class Bot with _$Bot {
const factory Bot({
required String id,
required String slug,
required bool isActive,
required String projectId,
required DateTime createdAt,
required DateTime updatedAt,
required SnAccount account,
SnDeveloper? developer,
}) = _Bot;
factory Bot.fromJson(Map<String, dynamic> json) => _$BotFromJson(json);
}
@freezed
sealed class BotConfig with _$BotConfig {
const factory BotConfig({
@Default(false) bool isPublic,
@Default(false) bool isInteractive,
@Default([]) List<String> allowedRealms,
@Default([]) List<String> allowedChatTypes,
@Default({}) Map<String, dynamic> metadata,
}) = _BotConfig;
factory BotConfig.fromJson(Map<String, dynamic> json) =>
_$BotConfigFromJson(json);
}
@freezed
sealed class BotLinks with _$BotLinks {
const factory BotLinks({
String? website,
String? documentation,
String? privacyPolicy,
String? termsOfService,
}) = _BotLinks;
factory BotLinks.fromJson(Map<String, dynamic> json) =>
_$BotLinksFromJson(json);
}
@freezed
sealed class BotSecret with _$BotSecret {
const factory BotSecret({
@Default('') String id,
@Default('') String secret,
String? description,
DateTime? expiredAt,
@Default('') String botId,
}) = _BotSecret;
factory BotSecret.fromJson(Map<String, dynamic> json) =>
_$BotSecretFromJson(json);
}

1156
lib/models/bot.freezed.dart Normal file

File diff suppressed because it is too large Load Diff

91
lib/models/bot.g.dart Normal file
View File

@@ -0,0 +1,91 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'bot.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_Bot _$BotFromJson(Map<String, dynamic> json) => _Bot(
id: json['id'] as String,
slug: json['slug'] as String,
isActive: json['is_active'] as bool,
projectId: json['project_id'] as String,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
account: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
developer:
json['developer'] == null
? null
: SnDeveloper.fromJson(json['developer'] as Map<String, dynamic>),
);
Map<String, dynamic> _$BotToJson(_Bot instance) => <String, dynamic>{
'id': instance.id,
'slug': instance.slug,
'is_active': instance.isActive,
'project_id': instance.projectId,
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'account': instance.account.toJson(),
'developer': instance.developer?.toJson(),
};
_BotConfig _$BotConfigFromJson(Map<String, dynamic> json) => _BotConfig(
isPublic: json['is_public'] as bool? ?? false,
isInteractive: json['is_interactive'] as bool? ?? false,
allowedRealms:
(json['allowed_realms'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
allowedChatTypes:
(json['allowed_chat_types'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
metadata: json['metadata'] as Map<String, dynamic>? ?? const {},
);
Map<String, dynamic> _$BotConfigToJson(_BotConfig instance) =>
<String, dynamic>{
'is_public': instance.isPublic,
'is_interactive': instance.isInteractive,
'allowed_realms': instance.allowedRealms,
'allowed_chat_types': instance.allowedChatTypes,
'metadata': instance.metadata,
};
_BotLinks _$BotLinksFromJson(Map<String, dynamic> json) => _BotLinks(
website: json['website'] as String?,
documentation: json['documentation'] as String?,
privacyPolicy: json['privacy_policy'] as String?,
termsOfService: json['terms_of_service'] as String?,
);
Map<String, dynamic> _$BotLinksToJson(_BotLinks instance) => <String, dynamic>{
'website': instance.website,
'documentation': instance.documentation,
'privacy_policy': instance.privacyPolicy,
'terms_of_service': instance.termsOfService,
};
_BotSecret _$BotSecretFromJson(Map<String, dynamic> json) => _BotSecret(
id: json['id'] as String? ?? '',
secret: json['secret'] as String? ?? '',
description: json['description'] as String?,
expiredAt:
json['expired_at'] == null
? null
: DateTime.parse(json['expired_at'] as String),
botId: json['bot_id'] as String? ?? '',
);
Map<String, dynamic> _$BotSecretToJson(_BotSecret instance) =>
<String, dynamic>{
'id': instance.id,
'secret': instance.secret,
'description': instance.description,
'expired_at': instance.expiredAt?.toIso8601String(),
'bot_id': instance.botId,
};

20
lib/models/bot_key.dart Normal file
View File

@@ -0,0 +1,20 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'bot_key.freezed.dart';
part 'bot_key.g.dart';
@freezed
sealed class SnAccountApiKey with _$SnAccountApiKey {
const factory SnAccountApiKey({
required String id,
required String label,
required String accountId,
required String sessionId,
required DateTime createdAt,
required DateTime updatedAt,
String? key,
}) = _SnAccountApiKey;
factory SnAccountApiKey.fromJson(Map<String, dynamic> json) =>
_$SnAccountApiKeyFromJson(json);
}

View File

@@ -0,0 +1,289 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'bot_key.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$SnAccountApiKey {
String get id; String get label; String get accountId; String get sessionId; DateTime get createdAt; DateTime get updatedAt; String? get key;
/// Create a copy of SnAccountApiKey
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$SnAccountApiKeyCopyWith<SnAccountApiKey> get copyWith => _$SnAccountApiKeyCopyWithImpl<SnAccountApiKey>(this as SnAccountApiKey, _$identity);
/// Serializes this SnAccountApiKey to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAccountApiKey&&(identical(other.id, id) || other.id == id)&&(identical(other.label, label) || other.label == label)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.sessionId, sessionId) || other.sessionId == sessionId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.key, key) || other.key == key));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,label,accountId,sessionId,createdAt,updatedAt,key);
@override
String toString() {
return 'SnAccountApiKey(id: $id, label: $label, accountId: $accountId, sessionId: $sessionId, createdAt: $createdAt, updatedAt: $updatedAt, key: $key)';
}
}
/// @nodoc
abstract mixin class $SnAccountApiKeyCopyWith<$Res> {
factory $SnAccountApiKeyCopyWith(SnAccountApiKey value, $Res Function(SnAccountApiKey) _then) = _$SnAccountApiKeyCopyWithImpl;
@useResult
$Res call({
String id, String label, String accountId, String sessionId, DateTime createdAt, DateTime updatedAt, String? key
});
}
/// @nodoc
class _$SnAccountApiKeyCopyWithImpl<$Res>
implements $SnAccountApiKeyCopyWith<$Res> {
_$SnAccountApiKeyCopyWithImpl(this._self, this._then);
final SnAccountApiKey _self;
final $Res Function(SnAccountApiKey) _then;
/// Create a copy of SnAccountApiKey
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? label = null,Object? accountId = null,Object? sessionId = null,Object? createdAt = null,Object? updatedAt = null,Object? key = freezed,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,label: null == label ? _self.label : label // ignore: cast_nullable_to_non_nullable
as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
as String,sessionId: null == sessionId ? _self.sessionId : sessionId // ignore: cast_nullable_to_non_nullable
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// Adds pattern-matching-related methods to [SnAccountApiKey].
extension SnAccountApiKeyPatterns on SnAccountApiKey {
/// 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( _SnAccountApiKey value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _SnAccountApiKey() 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( _SnAccountApiKey value) $default,){
final _that = this;
switch (_that) {
case _SnAccountApiKey():
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( _SnAccountApiKey value)? $default,){
final _that = this;
switch (_that) {
case _SnAccountApiKey() 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 id, String label, String accountId, String sessionId, DateTime createdAt, DateTime updatedAt, String? key)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SnAccountApiKey() when $default != null:
return $default(_that.id,_that.label,_that.accountId,_that.sessionId,_that.createdAt,_that.updatedAt,_that.key);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 id, String label, String accountId, String sessionId, DateTime createdAt, DateTime updatedAt, String? key) $default,) {final _that = this;
switch (_that) {
case _SnAccountApiKey():
return $default(_that.id,_that.label,_that.accountId,_that.sessionId,_that.createdAt,_that.updatedAt,_that.key);}
}
/// 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 id, String label, String accountId, String sessionId, DateTime createdAt, DateTime updatedAt, String? key)? $default,) {final _that = this;
switch (_that) {
case _SnAccountApiKey() when $default != null:
return $default(_that.id,_that.label,_that.accountId,_that.sessionId,_that.createdAt,_that.updatedAt,_that.key);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _SnAccountApiKey implements SnAccountApiKey {
const _SnAccountApiKey({required this.id, required this.label, required this.accountId, required this.sessionId, required this.createdAt, required this.updatedAt, this.key});
factory _SnAccountApiKey.fromJson(Map<String, dynamic> json) => _$SnAccountApiKeyFromJson(json);
@override final String id;
@override final String label;
@override final String accountId;
@override final String sessionId;
@override final DateTime createdAt;
@override final DateTime updatedAt;
@override final String? key;
/// Create a copy of SnAccountApiKey
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SnAccountApiKeyCopyWith<_SnAccountApiKey> get copyWith => __$SnAccountApiKeyCopyWithImpl<_SnAccountApiKey>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$SnAccountApiKeyToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAccountApiKey&&(identical(other.id, id) || other.id == id)&&(identical(other.label, label) || other.label == label)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.sessionId, sessionId) || other.sessionId == sessionId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.key, key) || other.key == key));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,label,accountId,sessionId,createdAt,updatedAt,key);
@override
String toString() {
return 'SnAccountApiKey(id: $id, label: $label, accountId: $accountId, sessionId: $sessionId, createdAt: $createdAt, updatedAt: $updatedAt, key: $key)';
}
}
/// @nodoc
abstract mixin class _$SnAccountApiKeyCopyWith<$Res> implements $SnAccountApiKeyCopyWith<$Res> {
factory _$SnAccountApiKeyCopyWith(_SnAccountApiKey value, $Res Function(_SnAccountApiKey) _then) = __$SnAccountApiKeyCopyWithImpl;
@override @useResult
$Res call({
String id, String label, String accountId, String sessionId, DateTime createdAt, DateTime updatedAt, String? key
});
}
/// @nodoc
class __$SnAccountApiKeyCopyWithImpl<$Res>
implements _$SnAccountApiKeyCopyWith<$Res> {
__$SnAccountApiKeyCopyWithImpl(this._self, this._then);
final _SnAccountApiKey _self;
final $Res Function(_SnAccountApiKey) _then;
/// Create a copy of SnAccountApiKey
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? label = null,Object? accountId = null,Object? sessionId = null,Object? createdAt = null,Object? updatedAt = null,Object? key = freezed,}) {
return _then(_SnAccountApiKey(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,label: null == label ? _self.label : label // ignore: cast_nullable_to_non_nullable
as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
as String,sessionId: null == sessionId ? _self.sessionId : sessionId // ignore: cast_nullable_to_non_nullable
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
// dart format on

29
lib/models/bot_key.g.dart Normal file
View File

@@ -0,0 +1,29 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'bot_key.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_SnAccountApiKey _$SnAccountApiKeyFromJson(Map<String, dynamic> json) =>
_SnAccountApiKey(
id: json['id'] as String,
label: json['label'] as String,
accountId: json['account_id'] as String,
sessionId: json['session_id'] as String,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
key: json['key'] as String?,
);
Map<String, dynamic> _$SnAccountApiKeyToJson(_SnAccountApiKey instance) =>
<String, dynamic>{
'id': instance.id,
'label': instance.label,
'account_id': instance.accountId,
'session_id': instance.sessionId,
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'key': instance.key,
};

View File

@@ -91,6 +91,7 @@ sealed class SnChatMember with _$SnChatMember {
required DateTime? breakUntil, required DateTime? breakUntil,
required DateTime? timeoutUntil, required DateTime? timeoutUntil,
required bool isBot, required bool isBot,
required SnAccountStatus? status,
// Frontend data // Frontend data
DateTime? lastTyped, DateTime? lastTyped,
}) = _SnChatMember; }) = _SnChatMember;
@@ -103,7 +104,7 @@ sealed class SnChatMember with _$SnChatMember {
sealed class SnChatSummary with _$SnChatSummary { sealed class SnChatSummary with _$SnChatSummary {
const factory SnChatSummary({ const factory SnChatSummary({
required int unreadCount, required int unreadCount,
required SnChatMessage lastMessage, required SnChatMessage? lastMessage,
}) = _SnChatSummary; }) = _SnChatSummary;
factory SnChatSummary.fromJson(Map<String, dynamic> json) => factory SnChatSummary.fromJson(Map<String, dynamic> json) =>

View File

@@ -1037,7 +1037,7 @@ $SnChatMemberCopyWith<$Res> get sender {
/// @nodoc /// @nodoc
mixin _$SnChatMember { mixin _$SnChatMember {
DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String get id; String get chatRoomId; SnChatRoom? get chatRoom; String get accountId; SnAccount get account; String? get nick; int get role; int get notify; DateTime? get joinedAt; DateTime? get breakUntil; DateTime? get timeoutUntil; bool get isBot;// Frontend data DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String get id; String get chatRoomId; SnChatRoom? get chatRoom; String get accountId; SnAccount get account; String? get nick; int get role; int get notify; DateTime? get joinedAt; DateTime? get breakUntil; DateTime? get timeoutUntil; bool get isBot; SnAccountStatus? get status;// Frontend data
DateTime? get lastTyped; DateTime? get lastTyped;
/// Create a copy of SnChatMember /// Create a copy of SnChatMember
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -1051,16 +1051,16 @@ $SnChatMemberCopyWith<SnChatMember> get copyWith => _$SnChatMemberCopyWithImpl<S
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnChatMember&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.chatRoomId, chatRoomId) || other.chatRoomId == chatRoomId)&&(identical(other.chatRoom, chatRoom) || other.chatRoom == chatRoom)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.role, role) || other.role == role)&&(identical(other.notify, notify) || other.notify == notify)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.breakUntil, breakUntil) || other.breakUntil == breakUntil)&&(identical(other.timeoutUntil, timeoutUntil) || other.timeoutUntil == timeoutUntil)&&(identical(other.isBot, isBot) || other.isBot == isBot)&&(identical(other.lastTyped, lastTyped) || other.lastTyped == lastTyped)); return identical(this, other) || (other.runtimeType == runtimeType&&other is SnChatMember&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.chatRoomId, chatRoomId) || other.chatRoomId == chatRoomId)&&(identical(other.chatRoom, chatRoom) || other.chatRoom == chatRoom)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.role, role) || other.role == role)&&(identical(other.notify, notify) || other.notify == notify)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.breakUntil, breakUntil) || other.breakUntil == breakUntil)&&(identical(other.timeoutUntil, timeoutUntil) || other.timeoutUntil == timeoutUntil)&&(identical(other.isBot, isBot) || other.isBot == isBot)&&(identical(other.status, status) || other.status == status)&&(identical(other.lastTyped, lastTyped) || other.lastTyped == lastTyped));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,chatRoomId,chatRoom,accountId,account,nick,role,notify,joinedAt,breakUntil,timeoutUntil,isBot,lastTyped); int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,chatRoomId,chatRoom,accountId,account,nick,role,notify,joinedAt,breakUntil,timeoutUntil,isBot,status,lastTyped);
@override @override
String toString() { String toString() {
return 'SnChatMember(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, chatRoomId: $chatRoomId, chatRoom: $chatRoom, accountId: $accountId, account: $account, nick: $nick, role: $role, notify: $notify, joinedAt: $joinedAt, breakUntil: $breakUntil, timeoutUntil: $timeoutUntil, isBot: $isBot, lastTyped: $lastTyped)'; return 'SnChatMember(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, chatRoomId: $chatRoomId, chatRoom: $chatRoom, accountId: $accountId, account: $account, nick: $nick, role: $role, notify: $notify, joinedAt: $joinedAt, breakUntil: $breakUntil, timeoutUntil: $timeoutUntil, isBot: $isBot, status: $status, lastTyped: $lastTyped)';
} }
@@ -1071,11 +1071,11 @@ abstract mixin class $SnChatMemberCopyWith<$Res> {
factory $SnChatMemberCopyWith(SnChatMember value, $Res Function(SnChatMember) _then) = _$SnChatMemberCopyWithImpl; factory $SnChatMemberCopyWith(SnChatMember value, $Res Function(SnChatMember) _then) = _$SnChatMemberCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String chatRoomId, SnChatRoom? chatRoom, String accountId, SnAccount account, String? nick, int role, int notify, DateTime? joinedAt, DateTime? breakUntil, DateTime? timeoutUntil, bool isBot, DateTime? lastTyped DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String chatRoomId, SnChatRoom? chatRoom, String accountId, SnAccount account, String? nick, int role, int notify, DateTime? joinedAt, DateTime? breakUntil, DateTime? timeoutUntil, bool isBot, SnAccountStatus? status, DateTime? lastTyped
}); });
$SnChatRoomCopyWith<$Res>? get chatRoom;$SnAccountCopyWith<$Res> get account; $SnChatRoomCopyWith<$Res>? get chatRoom;$SnAccountCopyWith<$Res> get account;$SnAccountStatusCopyWith<$Res>? get status;
} }
/// @nodoc /// @nodoc
@@ -1088,7 +1088,7 @@ class _$SnChatMemberCopyWithImpl<$Res>
/// Create a copy of SnChatMember /// Create a copy of SnChatMember
/// 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? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? chatRoomId = null,Object? chatRoom = freezed,Object? accountId = null,Object? account = null,Object? nick = freezed,Object? role = null,Object? notify = null,Object? joinedAt = freezed,Object? breakUntil = freezed,Object? timeoutUntil = freezed,Object? isBot = null,Object? lastTyped = freezed,}) { @pragma('vm:prefer-inline') @override $Res call({Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? chatRoomId = null,Object? chatRoom = freezed,Object? accountId = null,Object? account = null,Object? nick = freezed,Object? role = null,Object? notify = null,Object? joinedAt = freezed,Object? breakUntil = freezed,Object? timeoutUntil = freezed,Object? isBot = null,Object? status = freezed,Object? lastTyped = freezed,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable 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
@@ -1105,7 +1105,8 @@ as int,joinedAt: freezed == joinedAt ? _self.joinedAt : joinedAt // ignore: cast
as DateTime?,breakUntil: freezed == breakUntil ? _self.breakUntil : breakUntil // ignore: cast_nullable_to_non_nullable as DateTime?,breakUntil: freezed == breakUntil ? _self.breakUntil : breakUntil // ignore: cast_nullable_to_non_nullable
as DateTime?,timeoutUntil: freezed == timeoutUntil ? _self.timeoutUntil : timeoutUntil // ignore: cast_nullable_to_non_nullable as DateTime?,timeoutUntil: freezed == timeoutUntil ? _self.timeoutUntil : timeoutUntil // ignore: cast_nullable_to_non_nullable
as DateTime?,isBot: null == isBot ? _self.isBot : isBot // ignore: cast_nullable_to_non_nullable as DateTime?,isBot: null == isBot ? _self.isBot : isBot // ignore: cast_nullable_to_non_nullable
as bool,lastTyped: freezed == lastTyped ? _self.lastTyped : lastTyped // ignore: cast_nullable_to_non_nullable as bool,status: freezed == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as SnAccountStatus?,lastTyped: freezed == lastTyped ? _self.lastTyped : lastTyped // ignore: cast_nullable_to_non_nullable
as DateTime?, as DateTime?,
)); ));
} }
@@ -1130,6 +1131,18 @@ $SnAccountCopyWith<$Res> get account {
return $SnAccountCopyWith<$Res>(_self.account, (value) { return $SnAccountCopyWith<$Res>(_self.account, (value) {
return _then(_self.copyWith(account: value)); return _then(_self.copyWith(account: value));
}); });
}/// Create a copy of SnChatMember
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAccountStatusCopyWith<$Res>? get status {
if (_self.status == null) {
return null;
}
return $SnAccountStatusCopyWith<$Res>(_self.status!, (value) {
return _then(_self.copyWith(status: value));
});
} }
} }
@@ -1209,10 +1222,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String chatRoomId, SnChatRoom? chatRoom, String accountId, SnAccount account, String? nick, int role, int notify, DateTime? joinedAt, DateTime? breakUntil, DateTime? timeoutUntil, bool isBot, DateTime? lastTyped)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String chatRoomId, SnChatRoom? chatRoom, String accountId, SnAccount account, String? nick, int role, int notify, DateTime? joinedAt, DateTime? breakUntil, DateTime? timeoutUntil, bool isBot, SnAccountStatus? status, DateTime? lastTyped)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _SnChatMember() when $default != null: case _SnChatMember() when $default != null:
return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.id,_that.chatRoomId,_that.chatRoom,_that.accountId,_that.account,_that.nick,_that.role,_that.notify,_that.joinedAt,_that.breakUntil,_that.timeoutUntil,_that.isBot,_that.lastTyped);case _: return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.id,_that.chatRoomId,_that.chatRoom,_that.accountId,_that.account,_that.nick,_that.role,_that.notify,_that.joinedAt,_that.breakUntil,_that.timeoutUntil,_that.isBot,_that.status,_that.lastTyped);case _:
return orElse(); return orElse();
} }
@@ -1230,10 +1243,10 @@ return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.id,_that.c
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String chatRoomId, SnChatRoom? chatRoom, String accountId, SnAccount account, String? nick, int role, int notify, DateTime? joinedAt, DateTime? breakUntil, DateTime? timeoutUntil, bool isBot, DateTime? lastTyped) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String chatRoomId, SnChatRoom? chatRoom, String accountId, SnAccount account, String? nick, int role, int notify, DateTime? joinedAt, DateTime? breakUntil, DateTime? timeoutUntil, bool isBot, SnAccountStatus? status, DateTime? lastTyped) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnChatMember(): case _SnChatMember():
return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.id,_that.chatRoomId,_that.chatRoom,_that.accountId,_that.account,_that.nick,_that.role,_that.notify,_that.joinedAt,_that.breakUntil,_that.timeoutUntil,_that.isBot,_that.lastTyped);} return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.id,_that.chatRoomId,_that.chatRoom,_that.accountId,_that.account,_that.nick,_that.role,_that.notify,_that.joinedAt,_that.breakUntil,_that.timeoutUntil,_that.isBot,_that.status,_that.lastTyped);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
/// ///
@@ -1247,10 +1260,10 @@ return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.id,_that.c
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String chatRoomId, SnChatRoom? chatRoom, String accountId, SnAccount account, String? nick, int role, int notify, DateTime? joinedAt, DateTime? breakUntil, DateTime? timeoutUntil, bool isBot, DateTime? lastTyped)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String chatRoomId, SnChatRoom? chatRoom, String accountId, SnAccount account, String? nick, int role, int notify, DateTime? joinedAt, DateTime? breakUntil, DateTime? timeoutUntil, bool isBot, SnAccountStatus? status, DateTime? lastTyped)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnChatMember() when $default != null: case _SnChatMember() when $default != null:
return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.id,_that.chatRoomId,_that.chatRoom,_that.accountId,_that.account,_that.nick,_that.role,_that.notify,_that.joinedAt,_that.breakUntil,_that.timeoutUntil,_that.isBot,_that.lastTyped);case _: return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.id,_that.chatRoomId,_that.chatRoom,_that.accountId,_that.account,_that.nick,_that.role,_that.notify,_that.joinedAt,_that.breakUntil,_that.timeoutUntil,_that.isBot,_that.status,_that.lastTyped);case _:
return null; return null;
} }
@@ -1262,7 +1275,7 @@ return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.id,_that.c
@JsonSerializable() @JsonSerializable()
class _SnChatMember implements SnChatMember { class _SnChatMember implements SnChatMember {
const _SnChatMember({required this.createdAt, required this.updatedAt, required this.deletedAt, required this.id, required this.chatRoomId, required this.chatRoom, required this.accountId, required this.account, required this.nick, required this.role, required this.notify, required this.joinedAt, required this.breakUntil, required this.timeoutUntil, required this.isBot, this.lastTyped}); const _SnChatMember({required this.createdAt, required this.updatedAt, required this.deletedAt, required this.id, required this.chatRoomId, required this.chatRoom, required this.accountId, required this.account, required this.nick, required this.role, required this.notify, required this.joinedAt, required this.breakUntil, required this.timeoutUntil, required this.isBot, required this.status, this.lastTyped});
factory _SnChatMember.fromJson(Map<String, dynamic> json) => _$SnChatMemberFromJson(json); factory _SnChatMember.fromJson(Map<String, dynamic> json) => _$SnChatMemberFromJson(json);
@override final DateTime createdAt; @override final DateTime createdAt;
@@ -1280,6 +1293,7 @@ class _SnChatMember implements SnChatMember {
@override final DateTime? breakUntil; @override final DateTime? breakUntil;
@override final DateTime? timeoutUntil; @override final DateTime? timeoutUntil;
@override final bool isBot; @override final bool isBot;
@override final SnAccountStatus? status;
// Frontend data // Frontend data
@override final DateTime? lastTyped; @override final DateTime? lastTyped;
@@ -1296,16 +1310,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnChatMember&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.chatRoomId, chatRoomId) || other.chatRoomId == chatRoomId)&&(identical(other.chatRoom, chatRoom) || other.chatRoom == chatRoom)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.role, role) || other.role == role)&&(identical(other.notify, notify) || other.notify == notify)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.breakUntil, breakUntil) || other.breakUntil == breakUntil)&&(identical(other.timeoutUntil, timeoutUntil) || other.timeoutUntil == timeoutUntil)&&(identical(other.isBot, isBot) || other.isBot == isBot)&&(identical(other.lastTyped, lastTyped) || other.lastTyped == lastTyped)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnChatMember&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.chatRoomId, chatRoomId) || other.chatRoomId == chatRoomId)&&(identical(other.chatRoom, chatRoom) || other.chatRoom == chatRoom)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.role, role) || other.role == role)&&(identical(other.notify, notify) || other.notify == notify)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.breakUntil, breakUntil) || other.breakUntil == breakUntil)&&(identical(other.timeoutUntil, timeoutUntil) || other.timeoutUntil == timeoutUntil)&&(identical(other.isBot, isBot) || other.isBot == isBot)&&(identical(other.status, status) || other.status == status)&&(identical(other.lastTyped, lastTyped) || other.lastTyped == lastTyped));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,chatRoomId,chatRoom,accountId,account,nick,role,notify,joinedAt,breakUntil,timeoutUntil,isBot,lastTyped); int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,chatRoomId,chatRoom,accountId,account,nick,role,notify,joinedAt,breakUntil,timeoutUntil,isBot,status,lastTyped);
@override @override
String toString() { String toString() {
return 'SnChatMember(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, chatRoomId: $chatRoomId, chatRoom: $chatRoom, accountId: $accountId, account: $account, nick: $nick, role: $role, notify: $notify, joinedAt: $joinedAt, breakUntil: $breakUntil, timeoutUntil: $timeoutUntil, isBot: $isBot, lastTyped: $lastTyped)'; return 'SnChatMember(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, chatRoomId: $chatRoomId, chatRoom: $chatRoom, accountId: $accountId, account: $account, nick: $nick, role: $role, notify: $notify, joinedAt: $joinedAt, breakUntil: $breakUntil, timeoutUntil: $timeoutUntil, isBot: $isBot, status: $status, lastTyped: $lastTyped)';
} }
@@ -1316,11 +1330,11 @@ abstract mixin class _$SnChatMemberCopyWith<$Res> implements $SnChatMemberCopyWi
factory _$SnChatMemberCopyWith(_SnChatMember value, $Res Function(_SnChatMember) _then) = __$SnChatMemberCopyWithImpl; factory _$SnChatMemberCopyWith(_SnChatMember value, $Res Function(_SnChatMember) _then) = __$SnChatMemberCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String chatRoomId, SnChatRoom? chatRoom, String accountId, SnAccount account, String? nick, int role, int notify, DateTime? joinedAt, DateTime? breakUntil, DateTime? timeoutUntil, bool isBot, DateTime? lastTyped DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String chatRoomId, SnChatRoom? chatRoom, String accountId, SnAccount account, String? nick, int role, int notify, DateTime? joinedAt, DateTime? breakUntil, DateTime? timeoutUntil, bool isBot, SnAccountStatus? status, DateTime? lastTyped
}); });
@override $SnChatRoomCopyWith<$Res>? get chatRoom;@override $SnAccountCopyWith<$Res> get account; @override $SnChatRoomCopyWith<$Res>? get chatRoom;@override $SnAccountCopyWith<$Res> get account;@override $SnAccountStatusCopyWith<$Res>? get status;
} }
/// @nodoc /// @nodoc
@@ -1333,7 +1347,7 @@ class __$SnChatMemberCopyWithImpl<$Res>
/// Create a copy of SnChatMember /// Create a copy of SnChatMember
/// 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? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? chatRoomId = null,Object? chatRoom = freezed,Object? accountId = null,Object? account = null,Object? nick = freezed,Object? role = null,Object? notify = null,Object? joinedAt = freezed,Object? breakUntil = freezed,Object? timeoutUntil = freezed,Object? isBot = null,Object? lastTyped = freezed,}) { @override @pragma('vm:prefer-inline') $Res call({Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? chatRoomId = null,Object? chatRoom = freezed,Object? accountId = null,Object? account = null,Object? nick = freezed,Object? role = null,Object? notify = null,Object? joinedAt = freezed,Object? breakUntil = freezed,Object? timeoutUntil = freezed,Object? isBot = null,Object? status = freezed,Object? lastTyped = freezed,}) {
return _then(_SnChatMember( return _then(_SnChatMember(
createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable 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
@@ -1350,7 +1364,8 @@ as int,joinedAt: freezed == joinedAt ? _self.joinedAt : joinedAt // ignore: cast
as DateTime?,breakUntil: freezed == breakUntil ? _self.breakUntil : breakUntil // ignore: cast_nullable_to_non_nullable as DateTime?,breakUntil: freezed == breakUntil ? _self.breakUntil : breakUntil // ignore: cast_nullable_to_non_nullable
as DateTime?,timeoutUntil: freezed == timeoutUntil ? _self.timeoutUntil : timeoutUntil // ignore: cast_nullable_to_non_nullable as DateTime?,timeoutUntil: freezed == timeoutUntil ? _self.timeoutUntil : timeoutUntil // ignore: cast_nullable_to_non_nullable
as DateTime?,isBot: null == isBot ? _self.isBot : isBot // ignore: cast_nullable_to_non_nullable as DateTime?,isBot: null == isBot ? _self.isBot : isBot // ignore: cast_nullable_to_non_nullable
as bool,lastTyped: freezed == lastTyped ? _self.lastTyped : lastTyped // ignore: cast_nullable_to_non_nullable as bool,status: freezed == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as SnAccountStatus?,lastTyped: freezed == lastTyped ? _self.lastTyped : lastTyped // ignore: cast_nullable_to_non_nullable
as DateTime?, as DateTime?,
)); ));
} }
@@ -1376,6 +1391,18 @@ $SnAccountCopyWith<$Res> get account {
return $SnAccountCopyWith<$Res>(_self.account, (value) { return $SnAccountCopyWith<$Res>(_self.account, (value) {
return _then(_self.copyWith(account: value)); return _then(_self.copyWith(account: value));
}); });
}/// Create a copy of SnChatMember
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAccountStatusCopyWith<$Res>? get status {
if (_self.status == null) {
return null;
}
return $SnAccountStatusCopyWith<$Res>(_self.status!, (value) {
return _then(_self.copyWith(status: value));
});
} }
} }
@@ -1383,7 +1410,7 @@ $SnAccountCopyWith<$Res> get account {
/// @nodoc /// @nodoc
mixin _$SnChatSummary { mixin _$SnChatSummary {
int get unreadCount; SnChatMessage get lastMessage; int get unreadCount; SnChatMessage? get lastMessage;
/// Create a copy of SnChatSummary /// Create a copy of SnChatSummary
/// 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)
@@ -1416,11 +1443,11 @@ abstract mixin class $SnChatSummaryCopyWith<$Res> {
factory $SnChatSummaryCopyWith(SnChatSummary value, $Res Function(SnChatSummary) _then) = _$SnChatSummaryCopyWithImpl; factory $SnChatSummaryCopyWith(SnChatSummary value, $Res Function(SnChatSummary) _then) = _$SnChatSummaryCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
int unreadCount, SnChatMessage lastMessage int unreadCount, SnChatMessage? lastMessage
}); });
$SnChatMessageCopyWith<$Res> get lastMessage; $SnChatMessageCopyWith<$Res>? get lastMessage;
} }
/// @nodoc /// @nodoc
@@ -1433,20 +1460,23 @@ class _$SnChatSummaryCopyWithImpl<$Res>
/// Create a copy of SnChatSummary /// Create a copy of SnChatSummary
/// 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? unreadCount = null,Object? lastMessage = null,}) { @pragma('vm:prefer-inline') @override $Res call({Object? unreadCount = null,Object? lastMessage = freezed,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
unreadCount: null == unreadCount ? _self.unreadCount : unreadCount // ignore: cast_nullable_to_non_nullable unreadCount: null == unreadCount ? _self.unreadCount : unreadCount // ignore: cast_nullable_to_non_nullable
as int,lastMessage: null == lastMessage ? _self.lastMessage : lastMessage // ignore: cast_nullable_to_non_nullable as int,lastMessage: freezed == lastMessage ? _self.lastMessage : lastMessage // ignore: cast_nullable_to_non_nullable
as SnChatMessage, as SnChatMessage?,
)); ));
} }
/// Create a copy of SnChatSummary /// Create a copy of SnChatSummary
/// 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')
$SnChatMessageCopyWith<$Res> get lastMessage { $SnChatMessageCopyWith<$Res>? get lastMessage {
if (_self.lastMessage == null) {
return null;
}
return $SnChatMessageCopyWith<$Res>(_self.lastMessage, (value) { return $SnChatMessageCopyWith<$Res>(_self.lastMessage!, (value) {
return _then(_self.copyWith(lastMessage: value)); return _then(_self.copyWith(lastMessage: value));
}); });
} }
@@ -1528,7 +1558,7 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int unreadCount, SnChatMessage lastMessage)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int unreadCount, SnChatMessage? lastMessage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _SnChatSummary() when $default != null: case _SnChatSummary() when $default != null:
return $default(_that.unreadCount,_that.lastMessage);case _: return $default(_that.unreadCount,_that.lastMessage);case _:
@@ -1549,7 +1579,7 @@ return $default(_that.unreadCount,_that.lastMessage);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int unreadCount, SnChatMessage lastMessage) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int unreadCount, SnChatMessage? lastMessage) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnChatSummary(): case _SnChatSummary():
return $default(_that.unreadCount,_that.lastMessage);} return $default(_that.unreadCount,_that.lastMessage);}
@@ -1566,7 +1596,7 @@ return $default(_that.unreadCount,_that.lastMessage);}
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int unreadCount, SnChatMessage lastMessage)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int unreadCount, SnChatMessage? lastMessage)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnChatSummary() when $default != null: case _SnChatSummary() when $default != null:
return $default(_that.unreadCount,_that.lastMessage);case _: return $default(_that.unreadCount,_that.lastMessage);case _:
@@ -1585,7 +1615,7 @@ class _SnChatSummary implements SnChatSummary {
factory _SnChatSummary.fromJson(Map<String, dynamic> json) => _$SnChatSummaryFromJson(json); factory _SnChatSummary.fromJson(Map<String, dynamic> json) => _$SnChatSummaryFromJson(json);
@override final int unreadCount; @override final int unreadCount;
@override final SnChatMessage lastMessage; @override final SnChatMessage? lastMessage;
/// Create a copy of SnChatSummary /// Create a copy of SnChatSummary
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -1620,11 +1650,11 @@ abstract mixin class _$SnChatSummaryCopyWith<$Res> implements $SnChatSummaryCopy
factory _$SnChatSummaryCopyWith(_SnChatSummary value, $Res Function(_SnChatSummary) _then) = __$SnChatSummaryCopyWithImpl; factory _$SnChatSummaryCopyWith(_SnChatSummary value, $Res Function(_SnChatSummary) _then) = __$SnChatSummaryCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
int unreadCount, SnChatMessage lastMessage int unreadCount, SnChatMessage? lastMessage
}); });
@override $SnChatMessageCopyWith<$Res> get lastMessage; @override $SnChatMessageCopyWith<$Res>? get lastMessage;
} }
/// @nodoc /// @nodoc
@@ -1637,11 +1667,11 @@ class __$SnChatSummaryCopyWithImpl<$Res>
/// Create a copy of SnChatSummary /// Create a copy of SnChatSummary
/// 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? unreadCount = null,Object? lastMessage = null,}) { @override @pragma('vm:prefer-inline') $Res call({Object? unreadCount = null,Object? lastMessage = freezed,}) {
return _then(_SnChatSummary( return _then(_SnChatSummary(
unreadCount: null == unreadCount ? _self.unreadCount : unreadCount // ignore: cast_nullable_to_non_nullable unreadCount: null == unreadCount ? _self.unreadCount : unreadCount // ignore: cast_nullable_to_non_nullable
as int,lastMessage: null == lastMessage ? _self.lastMessage : lastMessage // ignore: cast_nullable_to_non_nullable as int,lastMessage: freezed == lastMessage ? _self.lastMessage : lastMessage // ignore: cast_nullable_to_non_nullable
as SnChatMessage, as SnChatMessage?,
)); ));
} }
@@ -1649,9 +1679,12 @@ as SnChatMessage,
/// 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')
$SnChatMessageCopyWith<$Res> get lastMessage { $SnChatMessageCopyWith<$Res>? get lastMessage {
if (_self.lastMessage == null) {
return null;
}
return $SnChatMessageCopyWith<$Res>(_self.lastMessage, (value) { return $SnChatMessageCopyWith<$Res>(_self.lastMessage!, (value) {
return _then(_self.copyWith(lastMessage: value)); return _then(_self.copyWith(lastMessage: value));
}); });
} }

View File

@@ -177,6 +177,12 @@ _SnChatMember _$SnChatMemberFromJson(Map<String, dynamic> json) =>
? null ? null
: DateTime.parse(json['timeout_until'] as String), : DateTime.parse(json['timeout_until'] as String),
isBot: json['is_bot'] as bool, isBot: json['is_bot'] as bool,
status:
json['status'] == null
? null
: SnAccountStatus.fromJson(
json['status'] as Map<String, dynamic>,
),
lastTyped: lastTyped:
json['last_typed'] == null json['last_typed'] == null
? null ? null
@@ -200,21 +206,25 @@ Map<String, dynamic> _$SnChatMemberToJson(_SnChatMember instance) =>
'break_until': instance.breakUntil?.toIso8601String(), 'break_until': instance.breakUntil?.toIso8601String(),
'timeout_until': instance.timeoutUntil?.toIso8601String(), 'timeout_until': instance.timeoutUntil?.toIso8601String(),
'is_bot': instance.isBot, 'is_bot': instance.isBot,
'status': instance.status?.toJson(),
'last_typed': instance.lastTyped?.toIso8601String(), 'last_typed': instance.lastTyped?.toIso8601String(),
}; };
_SnChatSummary _$SnChatSummaryFromJson(Map<String, dynamic> json) => _SnChatSummary _$SnChatSummaryFromJson(Map<String, dynamic> json) =>
_SnChatSummary( _SnChatSummary(
unreadCount: (json['unread_count'] as num).toInt(), unreadCount: (json['unread_count'] as num).toInt(),
lastMessage: SnChatMessage.fromJson( lastMessage:
json['last_message'] as Map<String, dynamic>, json['last_message'] == null
), ? null
: SnChatMessage.fromJson(
json['last_message'] as Map<String, dynamic>,
),
); );
Map<String, dynamic> _$SnChatSummaryToJson(_SnChatSummary instance) => Map<String, dynamic> _$SnChatSummaryToJson(_SnChatSummary instance) =>
<String, dynamic>{ <String, dynamic>{
'unread_count': instance.unreadCount, 'unread_count': instance.unreadCount,
'last_message': instance.lastMessage.toJson(), 'last_message': instance.lastMessage?.toJson(),
}; };
_MessageChange _$MessageChangeFromJson(Map<String, dynamic> json) => _MessageChange _$MessageChangeFromJson(Map<String, dynamic> json) =>

View File

@@ -0,0 +1,19 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'custom_app_secret.freezed.dart';
part 'custom_app_secret.g.dart';
@freezed
sealed class CustomAppSecret with _$CustomAppSecret {
const factory CustomAppSecret({
required String id,
required String? secret,
required DateTime createdAt,
String? description,
int? expiresIn,
bool? isOidc,
}) = _CustomAppSecret;
factory CustomAppSecret.fromJson(Map<String, dynamic> json) =>
_$CustomAppSecretFromJson(json);
}

View File

@@ -0,0 +1,286 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'custom_app_secret.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$CustomAppSecret {
String get id; String? get secret; DateTime get createdAt; String? get description; int? get expiresIn; bool? get isOidc;
/// Create a copy of CustomAppSecret
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$CustomAppSecretCopyWith<CustomAppSecret> get copyWith => _$CustomAppSecretCopyWithImpl<CustomAppSecret>(this as CustomAppSecret, _$identity);
/// Serializes this CustomAppSecret to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is CustomAppSecret&&(identical(other.id, id) || other.id == id)&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.description, description) || other.description == description)&&(identical(other.expiresIn, expiresIn) || other.expiresIn == expiresIn)&&(identical(other.isOidc, isOidc) || other.isOidc == isOidc));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,secret,createdAt,description,expiresIn,isOidc);
@override
String toString() {
return 'CustomAppSecret(id: $id, secret: $secret, createdAt: $createdAt, description: $description, expiresIn: $expiresIn, isOidc: $isOidc)';
}
}
/// @nodoc
abstract mixin class $CustomAppSecretCopyWith<$Res> {
factory $CustomAppSecretCopyWith(CustomAppSecret value, $Res Function(CustomAppSecret) _then) = _$CustomAppSecretCopyWithImpl;
@useResult
$Res call({
String id, String? secret, DateTime createdAt, String? description, int? expiresIn, bool? isOidc
});
}
/// @nodoc
class _$CustomAppSecretCopyWithImpl<$Res>
implements $CustomAppSecretCopyWith<$Res> {
_$CustomAppSecretCopyWithImpl(this._self, this._then);
final CustomAppSecret _self;
final $Res Function(CustomAppSecret) _then;
/// Create a copy of CustomAppSecret
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? secret = freezed,Object? createdAt = null,Object? description = freezed,Object? expiresIn = freezed,Object? isOidc = freezed,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,secret: freezed == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable
as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,expiresIn: freezed == expiresIn ? _self.expiresIn : expiresIn // ignore: cast_nullable_to_non_nullable
as int?,isOidc: freezed == isOidc ? _self.isOidc : isOidc // ignore: cast_nullable_to_non_nullable
as bool?,
));
}
}
/// Adds pattern-matching-related methods to [CustomAppSecret].
extension CustomAppSecretPatterns on CustomAppSecret {
/// 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( _CustomAppSecret value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _CustomAppSecret() 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( _CustomAppSecret value) $default,){
final _that = this;
switch (_that) {
case _CustomAppSecret():
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( _CustomAppSecret value)? $default,){
final _that = this;
switch (_that) {
case _CustomAppSecret() 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 id, String? secret, DateTime createdAt, String? description, int? expiresIn, bool? isOidc)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _CustomAppSecret() when $default != null:
return $default(_that.id,_that.secret,_that.createdAt,_that.description,_that.expiresIn,_that.isOidc);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 id, String? secret, DateTime createdAt, String? description, int? expiresIn, bool? isOidc) $default,) {final _that = this;
switch (_that) {
case _CustomAppSecret():
return $default(_that.id,_that.secret,_that.createdAt,_that.description,_that.expiresIn,_that.isOidc);}
}
/// 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 id, String? secret, DateTime createdAt, String? description, int? expiresIn, bool? isOidc)? $default,) {final _that = this;
switch (_that) {
case _CustomAppSecret() when $default != null:
return $default(_that.id,_that.secret,_that.createdAt,_that.description,_that.expiresIn,_that.isOidc);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _CustomAppSecret implements CustomAppSecret {
const _CustomAppSecret({required this.id, required this.secret, required this.createdAt, this.description, this.expiresIn, this.isOidc});
factory _CustomAppSecret.fromJson(Map<String, dynamic> json) => _$CustomAppSecretFromJson(json);
@override final String id;
@override final String? secret;
@override final DateTime createdAt;
@override final String? description;
@override final int? expiresIn;
@override final bool? isOidc;
/// Create a copy of CustomAppSecret
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$CustomAppSecretCopyWith<_CustomAppSecret> get copyWith => __$CustomAppSecretCopyWithImpl<_CustomAppSecret>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$CustomAppSecretToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CustomAppSecret&&(identical(other.id, id) || other.id == id)&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.description, description) || other.description == description)&&(identical(other.expiresIn, expiresIn) || other.expiresIn == expiresIn)&&(identical(other.isOidc, isOidc) || other.isOidc == isOidc));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,secret,createdAt,description,expiresIn,isOidc);
@override
String toString() {
return 'CustomAppSecret(id: $id, secret: $secret, createdAt: $createdAt, description: $description, expiresIn: $expiresIn, isOidc: $isOidc)';
}
}
/// @nodoc
abstract mixin class _$CustomAppSecretCopyWith<$Res> implements $CustomAppSecretCopyWith<$Res> {
factory _$CustomAppSecretCopyWith(_CustomAppSecret value, $Res Function(_CustomAppSecret) _then) = __$CustomAppSecretCopyWithImpl;
@override @useResult
$Res call({
String id, String? secret, DateTime createdAt, String? description, int? expiresIn, bool? isOidc
});
}
/// @nodoc
class __$CustomAppSecretCopyWithImpl<$Res>
implements _$CustomAppSecretCopyWith<$Res> {
__$CustomAppSecretCopyWithImpl(this._self, this._then);
final _CustomAppSecret _self;
final $Res Function(_CustomAppSecret) _then;
/// Create a copy of CustomAppSecret
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? secret = freezed,Object? createdAt = null,Object? description = freezed,Object? expiresIn = freezed,Object? isOidc = freezed,}) {
return _then(_CustomAppSecret(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,secret: freezed == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable
as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,expiresIn: freezed == expiresIn ? _self.expiresIn : expiresIn // ignore: cast_nullable_to_non_nullable
as int?,isOidc: freezed == isOidc ? _self.isOidc : isOidc // ignore: cast_nullable_to_non_nullable
as bool?,
));
}
}
// dart format on

View File

@@ -0,0 +1,27 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'custom_app_secret.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_CustomAppSecret _$CustomAppSecretFromJson(Map<String, dynamic> json) =>
_CustomAppSecret(
id: json['id'] as String,
secret: json['secret'] as String?,
createdAt: DateTime.parse(json['created_at'] as String),
description: json['description'] as String?,
expiresIn: (json['expires_in'] as num?)?.toInt(),
isOidc: json['is_oidc'] as bool?,
);
Map<String, dynamic> _$CustomAppSecretToJson(_CustomAppSecret instance) =>
<String, dynamic>{
'id': instance.id,
'secret': instance.secret,
'created_at': instance.createdAt.toIso8601String(),
'description': instance.description,
'expires_in': instance.expiresIn,
'is_oidc': instance.isOidc,
};

View File

@@ -0,0 +1,23 @@
class DevProject {
final String id;
final String slug;
final String name;
final String? description;
DevProject({
required this.id,
required this.slug,
required this.name,
this.description,
});
factory DevProject.fromJson(Map<String, dynamic> json) {
return DevProject(
id: json['id'],
slug: json['slug'],
name: json['name'],
description: json['description'],
);
}
}

View File

@@ -11,8 +11,8 @@ sealed class SnScrappedLink with _$SnScrappedLink {
required String title, required String title,
required String? description, required String? description,
required String? imageUrl, required String? imageUrl,
required String faviconUrl, required String? faviconUrl,
required String siteName, required String? siteName,
required String? contentType, required String? contentType,
required String? author, required String? author,
required DateTime? publishedDate, required DateTime? publishedDate,

View File

@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$SnScrappedLink { mixin _$SnScrappedLink {
String get type; String get url; String get title; String? get description; String? get imageUrl; String get faviconUrl; String get siteName; String? get contentType; String? get author; DateTime? get publishedDate; String get type; String get url; String get title; String? get description; String? get imageUrl; String? get faviconUrl; String? get siteName; String? get contentType; String? get author; DateTime? get publishedDate;
/// Create a copy of SnScrappedLink /// Create a copy of SnScrappedLink
/// 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)
@@ -48,7 +48,7 @@ abstract mixin class $SnScrappedLinkCopyWith<$Res> {
factory $SnScrappedLinkCopyWith(SnScrappedLink value, $Res Function(SnScrappedLink) _then) = _$SnScrappedLinkCopyWithImpl; factory $SnScrappedLinkCopyWith(SnScrappedLink value, $Res Function(SnScrappedLink) _then) = _$SnScrappedLinkCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String type, String url, String title, String? description, String? imageUrl, String faviconUrl, String siteName, String? contentType, String? author, DateTime? publishedDate String type, String url, String title, String? description, String? imageUrl, String? faviconUrl, String? siteName, String? contentType, String? author, DateTime? publishedDate
}); });
@@ -65,16 +65,16 @@ class _$SnScrappedLinkCopyWithImpl<$Res>
/// Create a copy of SnScrappedLink /// Create a copy of SnScrappedLink
/// 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? type = null,Object? url = null,Object? title = null,Object? description = freezed,Object? imageUrl = freezed,Object? faviconUrl = null,Object? siteName = null,Object? contentType = freezed,Object? author = freezed,Object? publishedDate = freezed,}) { @pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? url = null,Object? title = null,Object? description = freezed,Object? imageUrl = freezed,Object? faviconUrl = freezed,Object? siteName = freezed,Object? contentType = freezed,Object? author = freezed,Object? publishedDate = freezed,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
as String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable as String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable
as String,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable as String,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,imageUrl: freezed == imageUrl ? _self.imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable as String?,imageUrl: freezed == imageUrl ? _self.imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable
as String?,faviconUrl: null == faviconUrl ? _self.faviconUrl : faviconUrl // ignore: cast_nullable_to_non_nullable as String?,faviconUrl: freezed == faviconUrl ? _self.faviconUrl : faviconUrl // ignore: cast_nullable_to_non_nullable
as String,siteName: null == siteName ? _self.siteName : siteName // ignore: cast_nullable_to_non_nullable as String?,siteName: freezed == siteName ? _self.siteName : siteName // ignore: cast_nullable_to_non_nullable
as String,contentType: freezed == contentType ? _self.contentType : contentType // ignore: cast_nullable_to_non_nullable as String?,contentType: freezed == contentType ? _self.contentType : contentType // ignore: cast_nullable_to_non_nullable
as String?,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable as String?,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
as String?,publishedDate: freezed == publishedDate ? _self.publishedDate : publishedDate // ignore: cast_nullable_to_non_nullable as String?,publishedDate: freezed == publishedDate ? _self.publishedDate : publishedDate // ignore: cast_nullable_to_non_nullable
as DateTime?, as DateTime?,
@@ -159,7 +159,7 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String type, String url, String title, String? description, String? imageUrl, String faviconUrl, String siteName, String? contentType, String? author, DateTime? publishedDate)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String type, String url, String title, String? description, String? imageUrl, String? faviconUrl, String? siteName, String? contentType, String? author, DateTime? publishedDate)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _SnScrappedLink() when $default != null: case _SnScrappedLink() when $default != null:
return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUrl,_that.faviconUrl,_that.siteName,_that.contentType,_that.author,_that.publishedDate);case _: return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUrl,_that.faviconUrl,_that.siteName,_that.contentType,_that.author,_that.publishedDate);case _:
@@ -180,7 +180,7 @@ return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUr
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String type, String url, String title, String? description, String? imageUrl, String faviconUrl, String siteName, String? contentType, String? author, DateTime? publishedDate) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String type, String url, String title, String? description, String? imageUrl, String? faviconUrl, String? siteName, String? contentType, String? author, DateTime? publishedDate) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnScrappedLink(): case _SnScrappedLink():
return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUrl,_that.faviconUrl,_that.siteName,_that.contentType,_that.author,_that.publishedDate);} return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUrl,_that.faviconUrl,_that.siteName,_that.contentType,_that.author,_that.publishedDate);}
@@ -197,7 +197,7 @@ return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUr
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String type, String url, String title, String? description, String? imageUrl, String faviconUrl, String siteName, String? contentType, String? author, DateTime? publishedDate)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String type, String url, String title, String? description, String? imageUrl, String? faviconUrl, String? siteName, String? contentType, String? author, DateTime? publishedDate)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnScrappedLink() when $default != null: case _SnScrappedLink() when $default != null:
return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUrl,_that.faviconUrl,_that.siteName,_that.contentType,_that.author,_that.publishedDate);case _: return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUrl,_that.faviconUrl,_that.siteName,_that.contentType,_that.author,_that.publishedDate);case _:
@@ -220,8 +220,8 @@ class _SnScrappedLink implements SnScrappedLink {
@override final String title; @override final String title;
@override final String? description; @override final String? description;
@override final String? imageUrl; @override final String? imageUrl;
@override final String faviconUrl; @override final String? faviconUrl;
@override final String siteName; @override final String? siteName;
@override final String? contentType; @override final String? contentType;
@override final String? author; @override final String? author;
@override final DateTime? publishedDate; @override final DateTime? publishedDate;
@@ -259,7 +259,7 @@ abstract mixin class _$SnScrappedLinkCopyWith<$Res> implements $SnScrappedLinkCo
factory _$SnScrappedLinkCopyWith(_SnScrappedLink value, $Res Function(_SnScrappedLink) _then) = __$SnScrappedLinkCopyWithImpl; factory _$SnScrappedLinkCopyWith(_SnScrappedLink value, $Res Function(_SnScrappedLink) _then) = __$SnScrappedLinkCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String type, String url, String title, String? description, String? imageUrl, String faviconUrl, String siteName, String? contentType, String? author, DateTime? publishedDate String type, String url, String title, String? description, String? imageUrl, String? faviconUrl, String? siteName, String? contentType, String? author, DateTime? publishedDate
}); });
@@ -276,16 +276,16 @@ class __$SnScrappedLinkCopyWithImpl<$Res>
/// Create a copy of SnScrappedLink /// Create a copy of SnScrappedLink
/// 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? type = null,Object? url = null,Object? title = null,Object? description = freezed,Object? imageUrl = freezed,Object? faviconUrl = null,Object? siteName = null,Object? contentType = freezed,Object? author = freezed,Object? publishedDate = freezed,}) { @override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? url = null,Object? title = null,Object? description = freezed,Object? imageUrl = freezed,Object? faviconUrl = freezed,Object? siteName = freezed,Object? contentType = freezed,Object? author = freezed,Object? publishedDate = freezed,}) {
return _then(_SnScrappedLink( return _then(_SnScrappedLink(
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
as String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable as String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable
as String,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable as String,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,imageUrl: freezed == imageUrl ? _self.imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable as String?,imageUrl: freezed == imageUrl ? _self.imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable
as String?,faviconUrl: null == faviconUrl ? _self.faviconUrl : faviconUrl // ignore: cast_nullable_to_non_nullable as String?,faviconUrl: freezed == faviconUrl ? _self.faviconUrl : faviconUrl // ignore: cast_nullable_to_non_nullable
as String,siteName: null == siteName ? _self.siteName : siteName // ignore: cast_nullable_to_non_nullable as String?,siteName: freezed == siteName ? _self.siteName : siteName // ignore: cast_nullable_to_non_nullable
as String,contentType: freezed == contentType ? _self.contentType : contentType // ignore: cast_nullable_to_non_nullable as String?,contentType: freezed == contentType ? _self.contentType : contentType // ignore: cast_nullable_to_non_nullable
as String?,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable as String?,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
as String?,publishedDate: freezed == publishedDate ? _self.publishedDate : publishedDate // ignore: cast_nullable_to_non_nullable as String?,publishedDate: freezed == publishedDate ? _self.publishedDate : publishedDate // ignore: cast_nullable_to_non_nullable
as DateTime?, as DateTime?,

View File

@@ -13,8 +13,8 @@ _SnScrappedLink _$SnScrappedLinkFromJson(Map<String, dynamic> json) =>
title: json['title'] as String, title: json['title'] as String,
description: json['description'] as String?, description: json['description'] as String?,
imageUrl: json['image_url'] as String?, imageUrl: json['image_url'] as String?,
faviconUrl: json['favicon_url'] as String, faviconUrl: json['favicon_url'] as String?,
siteName: json['site_name'] as String, siteName: json['site_name'] as String?,
contentType: json['content_type'] as String?, contentType: json['content_type'] as String?,
author: json['author'] as String?, author: json['author'] as String?,
publishedDate: publishedDate:

View File

@@ -3,6 +3,7 @@ import 'package:island/models/file.dart';
import 'package:island/models/post_category.dart'; import 'package:island/models/post_category.dart';
import 'package:island/models/post_tag.dart'; import 'package:island/models/post_tag.dart';
import 'package:island/models/publisher.dart'; import 'package:island/models/publisher.dart';
import 'package:island/models/realm.dart';
part 'post.freezed.dart'; part 'post.freezed.dart';
part 'post.g.dart'; part 'post.g.dart';
@@ -18,6 +19,7 @@ sealed class SnPost with _$SnPost {
@Default(null) DateTime? publishedAt, @Default(null) DateTime? publishedAt,
@Default(0) int visibility, @Default(0) int visibility,
String? content, String? content,
String? slug,
@Default(0) int type, @Default(0) int type,
Map<String, dynamic>? meta, Map<String, dynamic>? meta,
@Default(0) int viewsUnique, @Default(0) int viewsUnique,
@@ -31,6 +33,8 @@ sealed class SnPost with _$SnPost {
SnPost? repliedPost, SnPost? repliedPost,
String? forwardedPostId, String? forwardedPostId,
SnPost? forwardedPost, SnPost? forwardedPost,
String? realmId,
SnRealm? realm,
@Default([]) List<SnCloudFile> attachments, @Default([]) List<SnCloudFile> attachments,
required SnPublisher publisher, required SnPublisher publisher,
@Default({}) Map<String, int> reactionsCount, @Default({}) Map<String, int> reactionsCount,

View File

@@ -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; int get type; Map<String, dynamic>? get meta; int get viewsUnique; int get viewsTotal; int get upvotes; int get downvotes; int get repliesCount; String? get threadedPostId; SnPost? get threadedPost; String? get repliedPostId; SnPost? get repliedPost; String? get forwardedPostId; SnPost? get forwardedPost; List<SnCloudFile> get attachments; SnPublisher get publisher; Map<String, int> get reactionsCount; 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; int get viewsUnique; int get viewsTotal; int get upvotes; int get downvotes; int get repliesCount; String? get threadedPostId; SnPost? get threadedPost; String? get repliedPostId; SnPost? get repliedPost; String? get forwardedPostId; SnPost? get forwardedPost; 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.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.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)&&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.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.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,type,const DeepCollectionEquality().hash(meta),viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,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),viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,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, type: $type, meta: $meta, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, 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, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, 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, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, 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, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, 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;$SnPublisherCopyWith<$Res> get publisher; $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? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? 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? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? 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
@@ -75,6 +75,7 @@ as String?,editedAt: freezed == editedAt ? _self.editedAt : editedAt // ignore:
as DateTime?,publishedAt: freezed == publishedAt ? _self.publishedAt : publishedAt // ignore: cast_nullable_to_non_nullable as DateTime?,publishedAt: freezed == publishedAt ? _self.publishedAt : publishedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,visibility: null == visibility ? _self.visibility : visibility // ignore: cast_nullable_to_non_nullable as DateTime?,visibility: null == visibility ? _self.visibility : visibility // ignore: cast_nullable_to_non_nullable
as int,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable as int,content: freezed == content ? _self.content : content // 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>?,viewsUnique: null == viewsUnique ? _self.viewsUnique : viewsUnique // ignore: cast_nullable_to_non_nullable
@@ -88,7 +89,9 @@ as SnPost?,repliedPostId: freezed == repliedPostId ? _self.repliedPostId : repli
as String?,repliedPost: freezed == repliedPost ? _self.repliedPost : repliedPost // ignore: cast_nullable_to_non_nullable as String?,repliedPost: freezed == repliedPost ? _self.repliedPost : repliedPost // ignore: cast_nullable_to_non_nullable
as SnPost?,forwardedPostId: freezed == forwardedPostId ? _self.forwardedPostId : forwardedPostId // ignore: cast_nullable_to_non_nullable as SnPost?,forwardedPostId: freezed == forwardedPostId ? _self.forwardedPostId : forwardedPostId // ignore: cast_nullable_to_non_nullable
as String?,forwardedPost: freezed == forwardedPost ? _self.forwardedPost : forwardedPost // ignore: cast_nullable_to_non_nullable as String?,forwardedPost: freezed == forwardedPost ? _self.forwardedPost : forwardedPost // ignore: cast_nullable_to_non_nullable
as SnPost?,attachments: null == attachments ? _self.attachments : attachments // ignore: cast_nullable_to_non_nullable as SnPost?,realmId: freezed == realmId ? _self.realmId : realmId // ignore: cast_nullable_to_non_nullable
as String?,realm: freezed == realm ? _self.realm : realm // ignore: cast_nullable_to_non_nullable
as SnRealm?,attachments: null == attachments ? _self.attachments : attachments // ignore: cast_nullable_to_non_nullable
as List<SnCloudFile>,publisher: null == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable as List<SnCloudFile>,publisher: null == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable
as SnPublisher,reactionsCount: null == reactionsCount ? _self.reactionsCount : reactionsCount // ignore: cast_nullable_to_non_nullable as SnPublisher,reactionsCount: null == reactionsCount ? _self.reactionsCount : reactionsCount // ignore: cast_nullable_to_non_nullable
as Map<String, int>,reactionsMade: null == reactionsMade ? _self.reactionsMade : reactionsMade // ignore: cast_nullable_to_non_nullable as Map<String, int>,reactionsMade: null == reactionsMade ? _self.reactionsMade : reactionsMade // ignore: cast_nullable_to_non_nullable
@@ -143,6 +146,18 @@ $SnPostCopyWith<$Res>? get forwardedPost {
/// 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')
$SnRealmCopyWith<$Res>? get realm {
if (_self.realm == null) {
return null;
}
return $SnRealmCopyWith<$Res>(_self.realm!, (value) {
return _then(_self.copyWith(realm: value));
});
}/// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnPublisherCopyWith<$Res> get publisher { $SnPublisherCopyWith<$Res> get publisher {
return $SnPublisherCopyWith<$Res>(_self.publisher, (value) { return $SnPublisherCopyWith<$Res>(_self.publisher, (value) {
@@ -227,10 +242,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, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, 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, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, 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.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_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.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_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();
} }
@@ -248,10 +263,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, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, 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, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, 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.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_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.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_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`
/// ///
@@ -265,10 +280,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, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, 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, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, 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.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_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.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_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;
} }
@@ -280,7 +295,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.type = 0, final Map<String, dynamic>? meta, this.viewsUnique = 0, this.viewsTotal = 0, this.upvotes = 0, this.downvotes = 0, this.repliesCount = 0, this.threadedPostId, this.threadedPost, this.repliedPostId, this.repliedPost, this.forwardedPostId, this.forwardedPost, final List<SnCloudFile> attachments = const [], 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.viewsUnique = 0, this.viewsTotal = 0, this.upvotes = 0, this.downvotes = 0, this.repliesCount = 0, this.threadedPostId, this.threadedPost, this.repliedPostId, this.repliedPost, this.forwardedPostId, this.forwardedPost, 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;
@@ -291,6 +306,7 @@ class _SnPost implements SnPost {
@override@JsonKey() final DateTime? publishedAt; @override@JsonKey() final DateTime? publishedAt;
@override@JsonKey() final int visibility; @override@JsonKey() final int visibility;
@override final String? content; @override final String? content;
@override final String? slug;
@override@JsonKey() final int type; @override@JsonKey() final int type;
final Map<String, dynamic>? _meta; final Map<String, dynamic>? _meta;
@override Map<String, dynamic>? get meta { @override Map<String, dynamic>? get meta {
@@ -312,6 +328,8 @@ class _SnPost implements SnPost {
@override final SnPost? repliedPost; @override final SnPost? repliedPost;
@override final String? forwardedPostId; @override final String? forwardedPostId;
@override final SnPost? forwardedPost; @override final SnPost? forwardedPost;
@override final String? realmId;
@override final SnRealm? realm;
final List<SnCloudFile> _attachments; final List<SnCloudFile> _attachments;
@override@JsonKey() List<SnCloudFile> get attachments { @override@JsonKey() List<SnCloudFile> get attachments {
if (_attachments is EqualUnmodifiableListView) return _attachments; if (_attachments is EqualUnmodifiableListView) return _attachments;
@@ -380,16 +398,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.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.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)&&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.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.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,type,const DeepCollectionEquality().hash(_meta),viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,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),viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,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, type: $type, meta: $meta, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, 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, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, 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)';
} }
@@ -400,11 +418,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, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, 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, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, 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 $SnPublisherCopyWith<$Res> get publisher; @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
@@ -417,7 +435,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? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? 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? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? 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
@@ -427,6 +445,7 @@ as String?,editedAt: freezed == editedAt ? _self.editedAt : editedAt // ignore:
as DateTime?,publishedAt: freezed == publishedAt ? _self.publishedAt : publishedAt // ignore: cast_nullable_to_non_nullable as DateTime?,publishedAt: freezed == publishedAt ? _self.publishedAt : publishedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,visibility: null == visibility ? _self.visibility : visibility // ignore: cast_nullable_to_non_nullable as DateTime?,visibility: null == visibility ? _self.visibility : visibility // ignore: cast_nullable_to_non_nullable
as int,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable as int,content: freezed == content ? _self.content : content // 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>?,viewsUnique: null == viewsUnique ? _self.viewsUnique : viewsUnique // ignore: cast_nullable_to_non_nullable
@@ -440,7 +459,9 @@ as SnPost?,repliedPostId: freezed == repliedPostId ? _self.repliedPostId : repli
as String?,repliedPost: freezed == repliedPost ? _self.repliedPost : repliedPost // ignore: cast_nullable_to_non_nullable as String?,repliedPost: freezed == repliedPost ? _self.repliedPost : repliedPost // ignore: cast_nullable_to_non_nullable
as SnPost?,forwardedPostId: freezed == forwardedPostId ? _self.forwardedPostId : forwardedPostId // ignore: cast_nullable_to_non_nullable as SnPost?,forwardedPostId: freezed == forwardedPostId ? _self.forwardedPostId : forwardedPostId // ignore: cast_nullable_to_non_nullable
as String?,forwardedPost: freezed == forwardedPost ? _self.forwardedPost : forwardedPost // ignore: cast_nullable_to_non_nullable as String?,forwardedPost: freezed == forwardedPost ? _self.forwardedPost : forwardedPost // ignore: cast_nullable_to_non_nullable
as SnPost?,attachments: null == attachments ? _self._attachments : attachments // ignore: cast_nullable_to_non_nullable as SnPost?,realmId: freezed == realmId ? _self.realmId : realmId // ignore: cast_nullable_to_non_nullable
as String?,realm: freezed == realm ? _self.realm : realm // ignore: cast_nullable_to_non_nullable
as SnRealm?,attachments: null == attachments ? _self._attachments : attachments // ignore: cast_nullable_to_non_nullable
as List<SnCloudFile>,publisher: null == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable as List<SnCloudFile>,publisher: null == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable
as SnPublisher,reactionsCount: null == reactionsCount ? _self._reactionsCount : reactionsCount // ignore: cast_nullable_to_non_nullable as SnPublisher,reactionsCount: null == reactionsCount ? _self._reactionsCount : reactionsCount // ignore: cast_nullable_to_non_nullable
as Map<String, int>,reactionsMade: null == reactionsMade ? _self._reactionsMade : reactionsMade // ignore: cast_nullable_to_non_nullable as Map<String, int>,reactionsMade: null == reactionsMade ? _self._reactionsMade : reactionsMade // ignore: cast_nullable_to_non_nullable
@@ -496,6 +517,18 @@ $SnPostCopyWith<$Res>? get forwardedPost {
/// 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')
$SnRealmCopyWith<$Res>? get realm {
if (_self.realm == null) {
return null;
}
return $SnRealmCopyWith<$Res>(_self.realm!, (value) {
return _then(_self.copyWith(realm: value));
});
}/// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnPublisherCopyWith<$Res> get publisher { $SnPublisherCopyWith<$Res> get publisher {
return $SnPublisherCopyWith<$Res>(_self.publisher, (value) { return $SnPublisherCopyWith<$Res>(_self.publisher, (value) {

View File

@@ -21,6 +21,7 @@ _SnPost _$SnPostFromJson(Map<String, dynamic> json) => _SnPost(
: DateTime.parse(json['published_at'] as String), : DateTime.parse(json['published_at'] as String),
visibility: (json['visibility'] as num?)?.toInt() ?? 0, visibility: (json['visibility'] as num?)?.toInt() ?? 0,
content: json['content'] as String?, content: json['content'] 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>?,
viewsUnique: (json['views_unique'] as num?)?.toInt() ?? 0, viewsUnique: (json['views_unique'] as num?)?.toInt() ?? 0,
@@ -43,6 +44,11 @@ _SnPost _$SnPostFromJson(Map<String, dynamic> json) => _SnPost(
json['forwarded_post'] == null json['forwarded_post'] == null
? null ? null
: SnPost.fromJson(json['forwarded_post'] as Map<String, dynamic>), : SnPost.fromJson(json['forwarded_post'] as Map<String, dynamic>),
realmId: json['realm_id'] as String?,
realm:
json['realm'] == null
? null
: SnRealm.fromJson(json['realm'] as Map<String, dynamic>),
attachments: attachments:
(json['attachments'] as List<dynamic>?) (json['attachments'] as List<dynamic>?)
?.map((e) => SnCloudFile.fromJson(e as Map<String, dynamic>)) ?.map((e) => SnCloudFile.fromJson(e as Map<String, dynamic>))
@@ -95,6 +101,7 @@ Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
'published_at': instance.publishedAt?.toIso8601String(), 'published_at': instance.publishedAt?.toIso8601String(),
'visibility': instance.visibility, 'visibility': instance.visibility,
'content': instance.content, 'content': instance.content,
'slug': instance.slug,
'type': instance.type, 'type': instance.type,
'meta': instance.meta, 'meta': instance.meta,
'views_unique': instance.viewsUnique, 'views_unique': instance.viewsUnique,
@@ -108,6 +115,8 @@ Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
'replied_post': instance.repliedPost?.toJson(), 'replied_post': instance.repliedPost?.toJson(),
'forwarded_post_id': instance.forwardedPostId, 'forwarded_post_id': instance.forwardedPostId,
'forwarded_post': instance.forwardedPost?.toJson(), 'forwarded_post': instance.forwardedPost?.toJson(),
'realm_id': instance.realmId,
'realm': instance.realm?.toJson(),
'attachments': instance.attachments.map((e) => e.toJson()).toList(), 'attachments': instance.attachments.map((e) => e.toJson()).toList(),
'publisher': instance.publisher.toJson(), 'publisher': instance.publisher.toJson(),
'reactions_count': instance.reactionsCount, 'reactions_count': instance.reactionsCount,

View File

@@ -15,6 +15,7 @@ sealed class SnPostCategory with _$SnPostCategory {
required String slug, required String slug,
String? name, String? name,
@Default([]) List<SnPost> posts, @Default([]) List<SnPost> posts,
@Default(0) int usage,
}) = _SnPostCategory; }) = _SnPostCategory;
factory SnPostCategory.fromJson(Map<String, dynamic> json) => factory SnPostCategory.fromJson(Map<String, dynamic> json) =>

View File

@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$SnPostCategory { mixin _$SnPostCategory {
String get id; String get slug; String? get name; List<SnPost> get posts; String get id; String get slug; String? get name; List<SnPost> get posts; int get usage;
/// Create a copy of SnPostCategory /// Create a copy of SnPostCategory
/// 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 @@ $SnPostCategoryCopyWith<SnPostCategory> get copyWith => _$SnPostCategoryCopyWith
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPostCategory&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other.posts, posts)); return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPostCategory&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other.posts, posts)&&(identical(other.usage, usage) || other.usage == usage));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(posts)); int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(posts),usage);
@override @override
String toString() { String toString() {
return 'SnPostCategory(id: $id, slug: $slug, name: $name, posts: $posts)'; return 'SnPostCategory(id: $id, slug: $slug, name: $name, posts: $posts, usage: $usage)';
} }
@@ -48,7 +48,7 @@ abstract mixin class $SnPostCategoryCopyWith<$Res> {
factory $SnPostCategoryCopyWith(SnPostCategory value, $Res Function(SnPostCategory) _then) = _$SnPostCategoryCopyWithImpl; factory $SnPostCategoryCopyWith(SnPostCategory value, $Res Function(SnPostCategory) _then) = _$SnPostCategoryCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String id, String slug, String? name, List<SnPost> posts String id, String slug, String? name, List<SnPost> posts, int usage
}); });
@@ -65,13 +65,14 @@ class _$SnPostCategoryCopyWithImpl<$Res>
/// Create a copy of SnPostCategory /// Create a copy of SnPostCategory
/// 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? slug = null,Object? name = freezed,Object? posts = null,}) { @pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,Object? usage = 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,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String?,posts: null == posts ? _self.posts : posts // ignore: cast_nullable_to_non_nullable as String?,posts: null == posts ? _self.posts : posts // ignore: cast_nullable_to_non_nullable
as List<SnPost>, as List<SnPost>,usage: null == usage ? _self.usage : usage // ignore: cast_nullable_to_non_nullable
as int,
)); ));
} }
@@ -153,10 +154,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts, int usage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _SnPostCategory() when $default != null: case _SnPostCategory() when $default != null:
return $default(_that.id,_that.slug,_that.name,_that.posts);case _: return $default(_that.id,_that.slug,_that.name,_that.posts,_that.usage);case _:
return orElse(); return orElse();
} }
@@ -174,10 +175,10 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts, int usage) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnPostCategory(): case _SnPostCategory():
return $default(_that.id,_that.slug,_that.name,_that.posts);} return $default(_that.id,_that.slug,_that.name,_that.posts,_that.usage);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
/// ///
@@ -191,10 +192,10 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);}
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String slug, String? name, List<SnPost> posts)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String slug, String? name, List<SnPost> posts, int usage)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnPostCategory() when $default != null: case _SnPostCategory() when $default != null:
return $default(_that.id,_that.slug,_that.name,_that.posts);case _: return $default(_that.id,_that.slug,_that.name,_that.posts,_that.usage);case _:
return null; return null;
} }
@@ -206,7 +207,7 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
@JsonSerializable() @JsonSerializable()
class _SnPostCategory extends SnPostCategory { class _SnPostCategory extends SnPostCategory {
const _SnPostCategory({required this.id, required this.slug, this.name, final List<SnPost> posts = const []}): _posts = posts,super._(); const _SnPostCategory({required this.id, required this.slug, this.name, final List<SnPost> posts = const [], this.usage = 0}): _posts = posts,super._();
factory _SnPostCategory.fromJson(Map<String, dynamic> json) => _$SnPostCategoryFromJson(json); factory _SnPostCategory.fromJson(Map<String, dynamic> json) => _$SnPostCategoryFromJson(json);
@override final String id; @override final String id;
@@ -219,6 +220,7 @@ class _SnPostCategory extends SnPostCategory {
return EqualUnmodifiableListView(_posts); return EqualUnmodifiableListView(_posts);
} }
@override@JsonKey() final int usage;
/// Create a copy of SnPostCategory /// Create a copy of SnPostCategory
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -233,16 +235,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPostCategory&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other._posts, _posts)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPostCategory&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other._posts, _posts)&&(identical(other.usage, usage) || other.usage == usage));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(_posts)); int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(_posts),usage);
@override @override
String toString() { String toString() {
return 'SnPostCategory(id: $id, slug: $slug, name: $name, posts: $posts)'; return 'SnPostCategory(id: $id, slug: $slug, name: $name, posts: $posts, usage: $usage)';
} }
@@ -253,7 +255,7 @@ abstract mixin class _$SnPostCategoryCopyWith<$Res> implements $SnPostCategoryCo
factory _$SnPostCategoryCopyWith(_SnPostCategory value, $Res Function(_SnPostCategory) _then) = __$SnPostCategoryCopyWithImpl; factory _$SnPostCategoryCopyWith(_SnPostCategory value, $Res Function(_SnPostCategory) _then) = __$SnPostCategoryCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String id, String slug, String? name, List<SnPost> posts String id, String slug, String? name, List<SnPost> posts, int usage
}); });
@@ -270,13 +272,14 @@ class __$SnPostCategoryCopyWithImpl<$Res>
/// Create a copy of SnPostCategory /// Create a copy of SnPostCategory
/// 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? slug = null,Object? name = freezed,Object? posts = null,}) { @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,Object? usage = null,}) {
return _then(_SnPostCategory( return _then(_SnPostCategory(
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,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String?,posts: null == posts ? _self._posts : posts // ignore: cast_nullable_to_non_nullable as String?,posts: null == posts ? _self._posts : posts // ignore: cast_nullable_to_non_nullable
as List<SnPost>, as List<SnPost>,usage: null == usage ? _self.usage : usage // ignore: cast_nullable_to_non_nullable
as int,
)); ));
} }

View File

@@ -16,6 +16,7 @@ _SnPostCategory _$SnPostCategoryFromJson(Map<String, dynamic> json) =>
?.map((e) => SnPost.fromJson(e as Map<String, dynamic>)) ?.map((e) => SnPost.fromJson(e as Map<String, dynamic>))
.toList() ?? .toList() ??
const [], const [],
usage: (json['usage'] as num?)?.toInt() ?? 0,
); );
Map<String, dynamic> _$SnPostCategoryToJson(_SnPostCategory instance) => Map<String, dynamic> _$SnPostCategoryToJson(_SnPostCategory instance) =>
@@ -24,4 +25,5 @@ Map<String, dynamic> _$SnPostCategoryToJson(_SnPostCategory instance) =>
'slug': instance.slug, 'slug': instance.slug,
'name': instance.name, 'name': instance.name,
'posts': instance.posts.map((e) => e.toJson()).toList(), 'posts': instance.posts.map((e) => e.toJson()).toList(),
'usage': instance.usage,
}; };

View File

@@ -11,6 +11,7 @@ sealed class SnPostTag with _$SnPostTag {
required String slug, required String slug,
String? name, String? name,
@Default([]) List<SnPost> posts, @Default([]) List<SnPost> posts,
@Default(0) int usage,
}) = _SnPostTag; }) = _SnPostTag;
factory SnPostTag.fromJson(Map<String, dynamic> json) => factory SnPostTag.fromJson(Map<String, dynamic> json) =>

View File

@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$SnPostTag { mixin _$SnPostTag {
String get id; String get slug; String? get name; List<SnPost> get posts; String get id; String get slug; String? get name; List<SnPost> get posts; int get usage;
/// Create a copy of SnPostTag /// Create a copy of SnPostTag
/// 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 @@ $SnPostTagCopyWith<SnPostTag> get copyWith => _$SnPostTagCopyWithImpl<SnPostTag>
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPostTag&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other.posts, posts)); return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPostTag&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other.posts, posts)&&(identical(other.usage, usage) || other.usage == usage));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(posts)); int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(posts),usage);
@override @override
String toString() { String toString() {
return 'SnPostTag(id: $id, slug: $slug, name: $name, posts: $posts)'; return 'SnPostTag(id: $id, slug: $slug, name: $name, posts: $posts, usage: $usage)';
} }
@@ -48,7 +48,7 @@ abstract mixin class $SnPostTagCopyWith<$Res> {
factory $SnPostTagCopyWith(SnPostTag value, $Res Function(SnPostTag) _then) = _$SnPostTagCopyWithImpl; factory $SnPostTagCopyWith(SnPostTag value, $Res Function(SnPostTag) _then) = _$SnPostTagCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String id, String slug, String? name, List<SnPost> posts String id, String slug, String? name, List<SnPost> posts, int usage
}); });
@@ -65,13 +65,14 @@ class _$SnPostTagCopyWithImpl<$Res>
/// Create a copy of SnPostTag /// Create a copy of SnPostTag
/// 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? slug = null,Object? name = freezed,Object? posts = null,}) { @pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,Object? usage = 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,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String?,posts: null == posts ? _self.posts : posts // ignore: cast_nullable_to_non_nullable as String?,posts: null == posts ? _self.posts : posts // ignore: cast_nullable_to_non_nullable
as List<SnPost>, as List<SnPost>,usage: null == usage ? _self.usage : usage // ignore: cast_nullable_to_non_nullable
as int,
)); ));
} }
@@ -153,10 +154,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts, int usage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _SnPostTag() when $default != null: case _SnPostTag() when $default != null:
return $default(_that.id,_that.slug,_that.name,_that.posts);case _: return $default(_that.id,_that.slug,_that.name,_that.posts,_that.usage);case _:
return orElse(); return orElse();
} }
@@ -174,10 +175,10 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts, int usage) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnPostTag(): case _SnPostTag():
return $default(_that.id,_that.slug,_that.name,_that.posts);} return $default(_that.id,_that.slug,_that.name,_that.posts,_that.usage);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
/// ///
@@ -191,10 +192,10 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);}
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String slug, String? name, List<SnPost> posts)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String slug, String? name, List<SnPost> posts, int usage)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnPostTag() when $default != null: case _SnPostTag() when $default != null:
return $default(_that.id,_that.slug,_that.name,_that.posts);case _: return $default(_that.id,_that.slug,_that.name,_that.posts,_that.usage);case _:
return null; return null;
} }
@@ -206,7 +207,7 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
@JsonSerializable() @JsonSerializable()
class _SnPostTag implements SnPostTag { class _SnPostTag implements SnPostTag {
const _SnPostTag({required this.id, required this.slug, this.name, final List<SnPost> posts = const []}): _posts = posts; const _SnPostTag({required this.id, required this.slug, this.name, final List<SnPost> posts = const [], this.usage = 0}): _posts = posts;
factory _SnPostTag.fromJson(Map<String, dynamic> json) => _$SnPostTagFromJson(json); factory _SnPostTag.fromJson(Map<String, dynamic> json) => _$SnPostTagFromJson(json);
@override final String id; @override final String id;
@@ -219,6 +220,7 @@ class _SnPostTag implements SnPostTag {
return EqualUnmodifiableListView(_posts); return EqualUnmodifiableListView(_posts);
} }
@override@JsonKey() final int usage;
/// Create a copy of SnPostTag /// Create a copy of SnPostTag
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -233,16 +235,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPostTag&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other._posts, _posts)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPostTag&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other._posts, _posts)&&(identical(other.usage, usage) || other.usage == usage));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(_posts)); int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(_posts),usage);
@override @override
String toString() { String toString() {
return 'SnPostTag(id: $id, slug: $slug, name: $name, posts: $posts)'; return 'SnPostTag(id: $id, slug: $slug, name: $name, posts: $posts, usage: $usage)';
} }
@@ -253,7 +255,7 @@ abstract mixin class _$SnPostTagCopyWith<$Res> implements $SnPostTagCopyWith<$Re
factory _$SnPostTagCopyWith(_SnPostTag value, $Res Function(_SnPostTag) _then) = __$SnPostTagCopyWithImpl; factory _$SnPostTagCopyWith(_SnPostTag value, $Res Function(_SnPostTag) _then) = __$SnPostTagCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String id, String slug, String? name, List<SnPost> posts String id, String slug, String? name, List<SnPost> posts, int usage
}); });
@@ -270,13 +272,14 @@ class __$SnPostTagCopyWithImpl<$Res>
/// Create a copy of SnPostTag /// Create a copy of SnPostTag
/// 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? slug = null,Object? name = freezed,Object? posts = null,}) { @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,Object? usage = null,}) {
return _then(_SnPostTag( return _then(_SnPostTag(
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,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String?,posts: null == posts ? _self._posts : posts // ignore: cast_nullable_to_non_nullable as String?,posts: null == posts ? _self._posts : posts // ignore: cast_nullable_to_non_nullable
as List<SnPost>, as List<SnPost>,usage: null == usage ? _self.usage : usage // ignore: cast_nullable_to_non_nullable
as int,
)); ));
} }

View File

@@ -15,6 +15,7 @@ _SnPostTag _$SnPostTagFromJson(Map<String, dynamic> json) => _SnPostTag(
?.map((e) => SnPost.fromJson(e as Map<String, dynamic>)) ?.map((e) => SnPost.fromJson(e as Map<String, dynamic>))
.toList() ?? .toList() ??
const [], const [],
usage: (json['usage'] as num?)?.toInt() ?? 0,
); );
Map<String, dynamic> _$SnPostTagToJson(_SnPostTag instance) => Map<String, dynamic> _$SnPostTagToJson(_SnPostTag instance) =>
@@ -23,4 +24,5 @@ Map<String, dynamic> _$SnPostTagToJson(_SnPostTag instance) =>
'slug': instance.slug, 'slug': instance.slug,
'name': instance.name, 'name': instance.name,
'posts': instance.posts.map((e) => e.toJson()).toList(), 'posts': instance.posts.map((e) => e.toJson()).toList(),
'usage': instance.usage,
}; };

View File

@@ -40,6 +40,7 @@ sealed class SnRealmMember with _$SnRealmMember {
required DateTime createdAt, required DateTime createdAt,
required DateTime updatedAt, required DateTime updatedAt,
required DateTime? deletedAt, required DateTime? deletedAt,
required SnAccountStatus? status,
}) = _SnRealmMember; }) = _SnRealmMember;
factory SnRealmMember.fromJson(Map<String, dynamic> json) => factory SnRealmMember.fromJson(Map<String, dynamic> json) =>

View File

@@ -359,7 +359,7 @@ $SnCloudFileCopyWith<$Res>? get background {
/// @nodoc /// @nodoc
mixin _$SnRealmMember { mixin _$SnRealmMember {
String get realmId; SnRealm? get realm; String get accountId; SnAccount? get account; int get role; DateTime? get joinedAt; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String get realmId; SnRealm? get realm; String get accountId; SnAccount? get account; int get role; DateTime? get joinedAt; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; SnAccountStatus? get status;
/// Create a copy of SnRealmMember /// Create a copy of SnRealmMember
/// 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)
@@ -372,16 +372,16 @@ $SnRealmMemberCopyWith<SnRealmMember> get copyWith => _$SnRealmMemberCopyWithImp
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnRealmMember&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.role, role) || other.role == role)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(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 SnRealmMember&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.role, role) || other.role == role)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.status, status) || other.status == status));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,realmId,realm,accountId,account,role,joinedAt,createdAt,updatedAt,deletedAt); int get hashCode => Object.hash(runtimeType,realmId,realm,accountId,account,role,joinedAt,createdAt,updatedAt,deletedAt,status);
@override @override
String toString() { String toString() {
return 'SnRealmMember(realmId: $realmId, realm: $realm, accountId: $accountId, account: $account, role: $role, joinedAt: $joinedAt, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; return 'SnRealmMember(realmId: $realmId, realm: $realm, accountId: $accountId, account: $account, role: $role, joinedAt: $joinedAt, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, status: $status)';
} }
@@ -392,11 +392,11 @@ abstract mixin class $SnRealmMemberCopyWith<$Res> {
factory $SnRealmMemberCopyWith(SnRealmMember value, $Res Function(SnRealmMember) _then) = _$SnRealmMemberCopyWithImpl; factory $SnRealmMemberCopyWith(SnRealmMember value, $Res Function(SnRealmMember) _then) = _$SnRealmMemberCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String realmId, SnRealm? realm, String accountId, SnAccount? account, int role, DateTime? joinedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt String realmId, SnRealm? realm, String accountId, SnAccount? account, int role, DateTime? joinedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, SnAccountStatus? status
}); });
$SnRealmCopyWith<$Res>? get realm;$SnAccountCopyWith<$Res>? get account; $SnRealmCopyWith<$Res>? get realm;$SnAccountCopyWith<$Res>? get account;$SnAccountStatusCopyWith<$Res>? get status;
} }
/// @nodoc /// @nodoc
@@ -409,7 +409,7 @@ class _$SnRealmMemberCopyWithImpl<$Res>
/// Create a copy of SnRealmMember /// Create a copy of SnRealmMember
/// 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? realmId = null,Object? realm = freezed,Object? accountId = null,Object? account = freezed,Object? role = null,Object? joinedAt = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { @pragma('vm:prefer-inline') @override $Res call({Object? realmId = null,Object? realm = freezed,Object? accountId = null,Object? account = freezed,Object? role = null,Object? joinedAt = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? status = freezed,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
realmId: null == realmId ? _self.realmId : realmId // ignore: cast_nullable_to_non_nullable realmId: null == realmId ? _self.realmId : realmId // ignore: cast_nullable_to_non_nullable
as String,realm: freezed == realm ? _self.realm : realm // ignore: cast_nullable_to_non_nullable as String,realm: freezed == realm ? _self.realm : realm // ignore: cast_nullable_to_non_nullable
@@ -420,7 +420,8 @@ as int,joinedAt: freezed == joinedAt ? _self.joinedAt : joinedAt // ignore: cast
as DateTime?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as DateTime?,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?,status: freezed == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as SnAccountStatus?,
)); ));
} }
/// Create a copy of SnRealmMember /// Create a copy of SnRealmMember
@@ -447,6 +448,18 @@ $SnAccountCopyWith<$Res>? get account {
return $SnAccountCopyWith<$Res>(_self.account!, (value) { return $SnAccountCopyWith<$Res>(_self.account!, (value) {
return _then(_self.copyWith(account: value)); return _then(_self.copyWith(account: value));
}); });
}/// Create a copy of SnRealmMember
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAccountStatusCopyWith<$Res>? get status {
if (_self.status == null) {
return null;
}
return $SnAccountStatusCopyWith<$Res>(_self.status!, (value) {
return _then(_self.copyWith(status: value));
});
} }
} }
@@ -526,10 +539,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String realmId, SnRealm? realm, String accountId, SnAccount? account, int role, DateTime? joinedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String realmId, SnRealm? realm, String accountId, SnAccount? account, int role, DateTime? joinedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, SnAccountStatus? status)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _SnRealmMember() when $default != null: case _SnRealmMember() when $default != null:
return $default(_that.realmId,_that.realm,_that.accountId,_that.account,_that.role,_that.joinedAt,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return $default(_that.realmId,_that.realm,_that.accountId,_that.account,_that.role,_that.joinedAt,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.status);case _:
return orElse(); return orElse();
} }
@@ -547,10 +560,10 @@ return $default(_that.realmId,_that.realm,_that.accountId,_that.account,_that.ro
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String realmId, SnRealm? realm, String accountId, SnAccount? account, int role, DateTime? joinedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String realmId, SnRealm? realm, String accountId, SnAccount? account, int role, DateTime? joinedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, SnAccountStatus? status) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnRealmMember(): case _SnRealmMember():
return $default(_that.realmId,_that.realm,_that.accountId,_that.account,_that.role,_that.joinedAt,_that.createdAt,_that.updatedAt,_that.deletedAt);} return $default(_that.realmId,_that.realm,_that.accountId,_that.account,_that.role,_that.joinedAt,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.status);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
/// ///
@@ -564,10 +577,10 @@ return $default(_that.realmId,_that.realm,_that.accountId,_that.account,_that.ro
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String realmId, SnRealm? realm, String accountId, SnAccount? account, int role, DateTime? joinedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String realmId, SnRealm? realm, String accountId, SnAccount? account, int role, DateTime? joinedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, SnAccountStatus? status)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnRealmMember() when $default != null: case _SnRealmMember() when $default != null:
return $default(_that.realmId,_that.realm,_that.accountId,_that.account,_that.role,_that.joinedAt,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return $default(_that.realmId,_that.realm,_that.accountId,_that.account,_that.role,_that.joinedAt,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.status);case _:
return null; return null;
} }
@@ -579,7 +592,7 @@ return $default(_that.realmId,_that.realm,_that.accountId,_that.account,_that.ro
@JsonSerializable() @JsonSerializable()
class _SnRealmMember implements SnRealmMember { class _SnRealmMember implements SnRealmMember {
const _SnRealmMember({required this.realmId, required this.realm, required this.accountId, required this.account, required this.role, required this.joinedAt, required this.createdAt, required this.updatedAt, required this.deletedAt}); const _SnRealmMember({required this.realmId, required this.realm, required this.accountId, required this.account, required this.role, required this.joinedAt, required this.createdAt, required this.updatedAt, required this.deletedAt, required this.status});
factory _SnRealmMember.fromJson(Map<String, dynamic> json) => _$SnRealmMemberFromJson(json); factory _SnRealmMember.fromJson(Map<String, dynamic> json) => _$SnRealmMemberFromJson(json);
@override final String realmId; @override final String realmId;
@@ -591,6 +604,7 @@ class _SnRealmMember implements SnRealmMember {
@override final DateTime createdAt; @override final DateTime createdAt;
@override final DateTime updatedAt; @override final DateTime updatedAt;
@override final DateTime? deletedAt; @override final DateTime? deletedAt;
@override final SnAccountStatus? status;
/// Create a copy of SnRealmMember /// Create a copy of SnRealmMember
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -605,16 +619,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnRealmMember&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.role, role) || other.role == role)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(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 _SnRealmMember&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.role, role) || other.role == role)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.status, status) || other.status == status));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,realmId,realm,accountId,account,role,joinedAt,createdAt,updatedAt,deletedAt); int get hashCode => Object.hash(runtimeType,realmId,realm,accountId,account,role,joinedAt,createdAt,updatedAt,deletedAt,status);
@override @override
String toString() { String toString() {
return 'SnRealmMember(realmId: $realmId, realm: $realm, accountId: $accountId, account: $account, role: $role, joinedAt: $joinedAt, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; return 'SnRealmMember(realmId: $realmId, realm: $realm, accountId: $accountId, account: $account, role: $role, joinedAt: $joinedAt, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, status: $status)';
} }
@@ -625,11 +639,11 @@ abstract mixin class _$SnRealmMemberCopyWith<$Res> implements $SnRealmMemberCopy
factory _$SnRealmMemberCopyWith(_SnRealmMember value, $Res Function(_SnRealmMember) _then) = __$SnRealmMemberCopyWithImpl; factory _$SnRealmMemberCopyWith(_SnRealmMember value, $Res Function(_SnRealmMember) _then) = __$SnRealmMemberCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String realmId, SnRealm? realm, String accountId, SnAccount? account, int role, DateTime? joinedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt String realmId, SnRealm? realm, String accountId, SnAccount? account, int role, DateTime? joinedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, SnAccountStatus? status
}); });
@override $SnRealmCopyWith<$Res>? get realm;@override $SnAccountCopyWith<$Res>? get account; @override $SnRealmCopyWith<$Res>? get realm;@override $SnAccountCopyWith<$Res>? get account;@override $SnAccountStatusCopyWith<$Res>? get status;
} }
/// @nodoc /// @nodoc
@@ -642,7 +656,7 @@ class __$SnRealmMemberCopyWithImpl<$Res>
/// Create a copy of SnRealmMember /// Create a copy of SnRealmMember
/// 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? realmId = null,Object? realm = freezed,Object? accountId = null,Object? account = freezed,Object? role = null,Object? joinedAt = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { @override @pragma('vm:prefer-inline') $Res call({Object? realmId = null,Object? realm = freezed,Object? accountId = null,Object? account = freezed,Object? role = null,Object? joinedAt = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? status = freezed,}) {
return _then(_SnRealmMember( return _then(_SnRealmMember(
realmId: null == realmId ? _self.realmId : realmId // ignore: cast_nullable_to_non_nullable realmId: null == realmId ? _self.realmId : realmId // ignore: cast_nullable_to_non_nullable
as String,realm: freezed == realm ? _self.realm : realm // ignore: cast_nullable_to_non_nullable as String,realm: freezed == realm ? _self.realm : realm // ignore: cast_nullable_to_non_nullable
@@ -653,7 +667,8 @@ as int,joinedAt: freezed == joinedAt ? _self.joinedAt : joinedAt // ignore: cast
as DateTime?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as DateTime?,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?,status: freezed == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as SnAccountStatus?,
)); ));
} }
@@ -681,6 +696,18 @@ $SnAccountCopyWith<$Res>? get account {
return $SnAccountCopyWith<$Res>(_self.account!, (value) { return $SnAccountCopyWith<$Res>(_self.account!, (value) {
return _then(_self.copyWith(account: value)); return _then(_self.copyWith(account: value));
}); });
}/// Create a copy of SnRealmMember
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAccountStatusCopyWith<$Res>? get status {
if (_self.status == null) {
return null;
}
return $SnAccountStatusCopyWith<$Res>(_self.status!, (value) {
return _then(_self.copyWith(status: value));
});
} }
} }

View File

@@ -75,6 +75,12 @@ _SnRealmMember _$SnRealmMemberFromJson(Map<String, dynamic> json) =>
json['deleted_at'] == null json['deleted_at'] == null
? null ? null
: DateTime.parse(json['deleted_at'] as String), : DateTime.parse(json['deleted_at'] as String),
status:
json['status'] == null
? null
: SnAccountStatus.fromJson(
json['status'] as Map<String, dynamic>,
),
); );
Map<String, dynamic> _$SnRealmMemberToJson(_SnRealmMember instance) => Map<String, dynamic> _$SnRealmMemberToJson(_SnRealmMember instance) =>
@@ -88,4 +94,5 @@ Map<String, dynamic> _$SnRealmMemberToJson(_SnRealmMember instance) =>
'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(),
'status': instance.status?.toJson(),
}; };

View File

@@ -23,6 +23,8 @@ const kAppSoundEffects = 'app_sound_effects';
const kAppAprilFoolFeatures = 'app_april_fool_features'; const kAppAprilFoolFeatures = 'app_april_fool_features';
const kAppWindowSize = 'app_window_size'; const kAppWindowSize = 'app_window_size';
const kAppEnterToSend = 'app_enter_to_send'; const kAppEnterToSend = 'app_enter_to_send';
const kFeaturedPostsCollapsedId =
'featured_posts_collapsed_id'; // Key for storing the ID of the collapsed featured post
const Map<String, FilterQuality> kImageQualityLevel = { const Map<String, FilterQuality> kImageQualityLevel = {
'settingsImageQualityLowest': FilterQuality.none, 'settingsImageQualityLowest': FilterQuality.none,

View File

@@ -1,6 +1,12 @@
import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'dart:io' show Platform;
import 'package:dio/dio.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_platform_alert/flutter_platform_alert.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart'; import 'package:island/models/account.dart';
@@ -13,13 +19,59 @@ class UserInfoNotifier extends StateNotifier<AsyncValue<SnAccount?>> {
UserInfoNotifier(this._ref) : super(const AsyncValue.data(null)); UserInfoNotifier(this._ref) : super(const AsyncValue.data(null));
Future<void> fetchUser() async { Future<void> fetchUser() async {
final token = _ref.watch(tokenProvider);
if (token == null) {
log('[UserInfo] No token found, not going to fetch...');
return;
}
try { try {
final client = _ref.read(apiClientProvider); final client = _ref.read(apiClientProvider);
final response = await client.get('/id/accounts/me'); final response = await client.get('/id/accounts/me');
final user = SnAccount.fromJson(response.data); final user = SnAccount.fromJson(response.data);
state = AsyncValue.data(user); state = AsyncValue.data(user);
FirebaseAnalytics.instance.setUserId(id: user.id);
if (kIsWeb || !Platform.isLinux) {
FirebaseAnalytics.instance.setUserId(id: user.id);
}
} catch (error, stackTrace) { } catch (error, stackTrace) {
if (!kIsWeb) {
if (error is DioException) {
FlutterPlatformAlert.showCustomAlert(
windowTitle: 'failedToLoadUserInfo'.tr(),
text: [
(error.response?.statusCode == 401
? 'failedToLoadUserInfoUnauthorized'
: 'failedToLoadUserInfoNetwork')
.tr()
.trim(),
'${error.response!.statusCode}\n${error.response?.headers}',
jsonEncode(error.response?.data),
].join('\n\n'),
iconStyle: IconStyle.error,
neutralButtonTitle: 'retry'.tr(),
negativeButtonTitle: 'okay'.tr(),
).then((value) {
if (value == CustomButton.neutralButton) {
fetchUser();
}
});
}
FlutterPlatformAlert.showCustomAlert(
windowTitle: 'failedToLoadUserInfo'.tr(),
text:
[
'failedToLoadUserInfoNetwork'.tr(),
error.toString(),
].join('\n\n').trim(),
iconStyle: IconStyle.error,
neutralButtonTitle: 'retry'.tr(),
negativeButtonTitle: 'okay'.tr(),
).then((value) {
if (value == CustomButton.neutralButton) {
fetchUser();
}
});
}
log( log(
"[UserInfo] Failed to fetch user info...", "[UserInfo] Failed to fetch user info...",
name: 'UserInfoNotifier', name: 'UserInfoNotifier',
@@ -35,7 +87,9 @@ class UserInfoNotifier extends StateNotifier<AsyncValue<SnAccount?>> {
final prefs = _ref.read(sharedPreferencesProvider); final prefs = _ref.read(sharedPreferencesProvider);
await prefs.remove(kTokenPairStoreKey); await prefs.remove(kTokenPairStoreKey);
_ref.invalidate(tokenProvider); _ref.invalidate(tokenProvider);
FirebaseAnalytics.instance.setUserId(id: null); if (kIsWeb || !Platform.isLinux) {
FirebaseAnalytics.instance.setUserId(id: null);
}
} }
} }

View File

@@ -1,14 +1,25 @@
import 'dart:io' show Platform;
import 'package:animations/animations.dart';
import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/screens/about.dart'; import 'package:island/screens/about.dart';
import 'package:island/screens/developers/apps.dart'; import 'package:island/screens/account/credits.dart';
import 'package:island/screens/developers/app_detail.dart';
import 'package:island/screens/developers/bot_detail.dart';
import 'package:island/screens/developers/edit_app.dart'; import 'package:island/screens/developers/edit_app.dart';
import 'package:island/screens/developers/edit_bot.dart';
import 'package:island/screens/developers/new_app.dart'; import 'package:island/screens/developers/new_app.dart';
import 'package:island/screens/developers/hub.dart'; import 'package:island/screens/developers/hub.dart';
import 'package:island/screens/developers/new_bot.dart';
import 'package:island/screens/developers/projects.dart';
import 'package:island/screens/developers/edit_project.dart';
import 'package:island/screens/developers/new_project.dart';
import 'package:island/screens/developers/project_detail.dart';
import 'package:island/screens/discovery/articles.dart'; import 'package:island/screens/discovery/articles.dart';
import 'package:island/screens/posts/post_categories_list.dart';
import 'package:island/screens/posts/post_category_detail.dart'; import 'package:island/screens/posts/post_category_detail.dart';
import 'package:island/screens/posts/post_search.dart'; import 'package:island/screens/posts/post_search.dart';
import 'package:island/widgets/app_wrapper.dart'; import 'package:island/widgets/app_wrapper.dart';
@@ -20,9 +31,9 @@ import 'package:island/screens/notification.dart';
import 'package:island/screens/wallet.dart'; import 'package:island/screens/wallet.dart';
import 'package:island/screens/account/relationship.dart'; import 'package:island/screens/account/relationship.dart';
import 'package:island/screens/account/profile.dart'; import 'package:island/screens/account/profile.dart';
import 'package:island/screens/account/me/update.dart'; import 'package:island/screens/account/me/profile_update.dart';
import 'package:island/screens/account/leveling.dart'; import 'package:island/screens/account/leveling.dart';
import 'package:island/screens/account/me/settings.dart'; import 'package:island/screens/account/me/account_settings.dart';
import 'package:island/screens/chat/chat.dart'; import 'package:island/screens/chat/chat.dart';
import 'package:island/screens/chat/room.dart'; import 'package:island/screens/chat/room.dart';
import 'package:island/screens/chat/room_detail.dart'; import 'package:island/screens/chat/room_detail.dart';
@@ -31,8 +42,10 @@ import 'package:island/screens/creators/hub.dart';
import 'package:island/screens/creators/posts/post_manage_list.dart'; import 'package:island/screens/creators/posts/post_manage_list.dart';
import 'package:island/screens/creators/stickers/stickers.dart'; import 'package:island/screens/creators/stickers/stickers.dart';
import 'package:island/screens/creators/stickers/pack_detail.dart'; import 'package:island/screens/creators/stickers/pack_detail.dart';
import 'package:island/screens/stickers/marketplace.dart'; import 'package:island/screens/stickers/sticker_marketplace.dart';
import 'package:island/screens/stickers/pack_detail.dart'; import 'package:island/screens/stickers/pack_detail.dart';
import 'package:island/screens/discovery/feeds/feed_marketplace.dart';
import 'package:island/screens/discovery/feeds/feed_detail.dart';
import 'package:island/screens/creators/poll/poll_list.dart'; import 'package:island/screens/creators/poll/poll_list.dart';
import 'package:island/screens/creators/publishers.dart'; import 'package:island/screens/creators/publishers.dart';
import 'package:island/screens/creators/webfeed/webfeed_list.dart'; import 'package:island/screens/creators/webfeed/webfeed_list.dart';
@@ -50,19 +63,42 @@ import 'package:island/screens/account/event_calendar.dart';
import 'package:island/screens/discovery/realms.dart'; import 'package:island/screens/discovery/realms.dart';
import 'package:island/screens/reports/report_detail.dart'; import 'package:island/screens/reports/report_detail.dart';
import 'package:island/screens/reports/report_list.dart'; import 'package:island/screens/reports/report_list.dart';
import 'package:island/widgets/post/post_shuffle.dart';
// Shell route keys for nested navigation // Shell route keys for nested navigation
final rootNavigatorKey = GlobalKey<NavigatorState>(); final rootNavigatorKey = GlobalKey<NavigatorState>();
final _shellNavigatorKey = GlobalKey<NavigatorState>(); final _shellNavigatorKey = GlobalKey<NavigatorState>();
final _tabsShellKey = GlobalKey<NavigatorState>(); final _tabsShellKey = GlobalKey<NavigatorState>();
Widget _tabPagesTransitionBuilder(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return FadeThroughTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
fillColor: Theme.of(context).colorScheme.surface,
child: child,
);
}
bool get _supportsAnalytics =>
kIsWeb ||
Platform.isAndroid ||
Platform.isIOS ||
Platform.isMacOS ||
Platform.isWindows;
// Provider for the router // Provider for the router
final routerProvider = Provider<GoRouter>((ref) { final routerProvider = Provider<GoRouter>((ref) {
return GoRouter( return GoRouter(
navigatorKey: rootNavigatorKey, navigatorKey: rootNavigatorKey,
initialLocation: '/', initialLocation: '/',
observers: [ observers: [
FirebaseAnalyticsObserver(analytics: FirebaseAnalytics.instance), if (_supportsAnalytics)
FirebaseAnalyticsObserver(analytics: FirebaseAnalytics.instance),
], ],
routes: [ routes: [
ShellRoute( ShellRoute(
@@ -262,30 +298,99 @@ final routerProvider = Provider<GoRouter>((ref) {
builder: (context, state) => const DeveloperHubScreen(), builder: (context, state) => const DeveloperHubScreen(),
), ),
GoRoute( GoRoute(
name: 'developerApps', name: 'developerProjects',
path: '/developers/:name/apps', path: '/developers/:name/projects',
builder: builder:
(context, state) => CustomAppsScreen( (context, state) => DevProjectsScreen(
publisherName: state.pathParameters['name']!, publisherName: state.pathParameters['name']!,
), ),
), ),
GoRoute( GoRoute(
name: 'developerAppNew', name: 'developerProjectNew',
path: '/developers/:name/apps/new', path: '/developers/:name/projects/new',
builder: builder:
(context, state) => NewCustomAppScreen( (context, state) => NewProjectScreen(
publisherName: state.pathParameters['name']!, publisherName: state.pathParameters['name']!,
), ),
), ),
GoRoute( GoRoute(
name: 'developerAppEdit', name: 'developerProjectEdit',
path: '/developers/:name/apps/:id', path: '/developers/:name/projects/:id/edit',
builder: builder:
(context, state) => EditAppScreen( (context, state) => EditProjectScreen(
publisherName: state.pathParameters['name']!, publisherName: state.pathParameters['name']!,
id: state.pathParameters['id']!, id: state.pathParameters['id']!,
), ),
), ),
GoRoute(
name: 'developerProjectDetail',
path: '/developers/:name/projects/:projectId',
builder:
(context, state) => ProjectDetailScreen(
publisherName: state.pathParameters['name']!,
projectId: state.pathParameters['projectId']!,
),
routes: [
GoRoute(
name: 'developerAppNew',
path: 'apps/new',
builder:
(context, state) => NewCustomAppScreen(
publisherName: state.pathParameters['name']!,
projectId: state.pathParameters['projectId']!,
),
),
GoRoute(
name: 'developerAppEdit',
path: 'apps/:id/edit',
builder:
(context, state) => EditAppScreen(
publisherName: state.pathParameters['name']!,
projectId: state.pathParameters['projectId']!,
id: state.pathParameters['id']!,
),
),
GoRoute(
name: 'developerAppDetail',
path: 'apps/:appId',
builder:
(context, state) => AppDetailScreen(
publisherName: state.pathParameters['name']!,
projectId: state.pathParameters['projectId']!,
appId: state.pathParameters['appId']!,
),
),
GoRoute(
name: 'developerBotDetail',
path: 'bots/:botId',
builder:
(context, state) => BotDetailScreen(
publisherName: state.pathParameters['name']!,
projectId: state.pathParameters['projectId']!,
botId: state.pathParameters['botId']!,
),
),
GoRoute(
name: 'developerBotNew',
path: 'bots/new',
builder:
(context, state) => NewBotScreen(
publisherName: state.pathParameters['name']!,
projectId: state.pathParameters['projectId']!,
),
),
GoRoute(
name: 'developerBotEdit',
path: 'bots/:id/edit',
builder:
(context, state) => EditBotScreen(
publisherName: state.pathParameters['name']!,
projectId: state.pathParameters['projectId']!,
id: state.pathParameters['id']!,
),
),
],
),
], ],
), ),
@@ -339,7 +444,12 @@ final routerProvider = Provider<GoRouter>((ref) {
GoRoute( GoRoute(
name: 'explore', name: 'explore',
path: '/', path: '/',
builder: (context, state) => const ExploreScreen(), pageBuilder:
(context, state) => CustomTransitionPage(
key: const ValueKey('explore'),
child: const ExploreScreen(),
transitionsBuilder: _tabPagesTransitionBuilder,
),
), ),
GoRoute( GoRoute(
name: 'postSearch', name: 'postSearch',
@@ -347,12 +457,14 @@ final routerProvider = Provider<GoRouter>((ref) {
builder: (context, state) => const PostSearchScreen(), builder: (context, state) => const PostSearchScreen(),
), ),
GoRoute( GoRoute(
name: 'postDetail', name: 'postShuffle',
path: '/posts/:id', path: '/posts/shuffle',
builder: (context, state) { builder: (context, state) => const PostShuffleScreen(),
final id = state.pathParameters['id']!; ),
return PostDetailScreen(id: id); GoRoute(
}, name: 'postCategories',
path: '/posts/categories',
builder: (context, state) => const PostCategoriesListScreen(),
), ),
GoRoute( GoRoute(
name: 'postCategoryDetail', name: 'postCategoryDetail',
@@ -362,6 +474,11 @@ final routerProvider = Provider<GoRouter>((ref) {
return PostCategoryDetailScreen(slug: slug, isCategory: true); return PostCategoryDetailScreen(slug: slug, isCategory: true);
}, },
), ),
GoRoute(
name: 'postTags',
path: '/posts/tags',
builder: (context, state) => const PostTagsListScreen(),
),
GoRoute( GoRoute(
name: 'postTagDetail', name: 'postTagDetail',
path: '/posts/tags/:slug', path: '/posts/tags/:slug',
@@ -373,6 +490,14 @@ final routerProvider = Provider<GoRouter>((ref) {
); );
}, },
), ),
GoRoute(
name: 'postDetail',
path: '/posts/:id',
builder: (context, state) {
final id = state.pathParameters['id']!;
return PostDetailScreen(id: id);
},
),
GoRoute( GoRoute(
name: 'publisherProfile', name: 'publisherProfile',
path: '/publishers/:name', path: '/publishers/:name',
@@ -389,8 +514,12 @@ final routerProvider = Provider<GoRouter>((ref) {
// Chat tab // Chat tab
ShellRoute( ShellRoute(
builder: pageBuilder:
(context, state, child) => ChatShellScreen(child: child), (context, state, child) => CustomTransitionPage(
key: const ValueKey('chat'),
child: ChatShellScreen(child: child),
transitionsBuilder: _tabPagesTransitionBuilder,
),
routes: [ routes: [
GoRoute( GoRoute(
name: 'chatList', name: 'chatList',
@@ -433,7 +562,12 @@ final routerProvider = Provider<GoRouter>((ref) {
GoRoute( GoRoute(
name: 'realmList', name: 'realmList',
path: '/realms', path: '/realms',
builder: (context, state) => const RealmListScreen(), pageBuilder:
(context, state) => CustomTransitionPage(
key: const ValueKey('realms'),
child: const RealmListScreen(),
transitionsBuilder: _tabPagesTransitionBuilder,
),
routes: [ routes: [
GoRoute( GoRoute(
name: 'realmNew', name: 'realmNew',
@@ -461,8 +595,12 @@ final routerProvider = Provider<GoRouter>((ref) {
// Account tab // Account tab
ShellRoute( ShellRoute(
builder: pageBuilder:
(context, state, child) => AccountShellScreen(child: child), (context, state, child) => CustomTransitionPage(
key: const ValueKey('account'),
child: AccountShellScreen(child: child),
transitionsBuilder: _tabPagesTransitionBuilder,
),
routes: [ routes: [
GoRoute( GoRoute(
name: 'account', name: 'account',
@@ -486,6 +624,22 @@ final routerProvider = Provider<GoRouter>((ref) {
), ),
], ],
), ),
GoRoute(
name: 'webFeedMarketplace',
path: '/feeds',
builder:
(context, state) => const MarketplaceWebFeedsScreen(),
routes: [
GoRoute(
name: 'webFeedDetail',
path: ':feedId',
builder: (context, state) {
final feedId = state.pathParameters['feedId']!;
return MarketplaceWebFeedDetailScreen(id: feedId);
},
),
],
),
GoRoute( GoRoute(
name: 'notifications', name: 'notifications',
path: '/account/notifications', path: '/account/notifications',
@@ -496,6 +650,11 @@ final routerProvider = Provider<GoRouter>((ref) {
path: '/account/wallet', path: '/account/wallet',
builder: (context, state) => const WalletScreen(), builder: (context, state) => const WalletScreen(),
), ),
GoRoute(
name: 'socialCredits',
path: '/account/credits',
builder: (context, state) => const SocialCreditsScreen(),
),
GoRoute( GoRoute(
name: 'relationships', name: 'relationships',
path: '/account/relationships', path: '/account/relationships',

View File

@@ -178,7 +178,8 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
context, context,
icon: Symbols.label, icon: Symbols.label,
label: 'aboutDeviceName'.tr(), label: 'aboutDeviceName'.tr(),
value: _deviceInfo?.data['name'], value:
_deviceInfo?.data['name'] ?? 'unknown'.tr(),
), ),
_buildInfoItem( _buildInfoItem(
context, context,

View File

@@ -236,6 +236,26 @@ class AccountScreen extends HookConsumerWidget {
context.pushNamed('stickerMarketplace'); context.pushNamed('stickerMarketplace');
}, },
), ),
ListTile(
minTileHeight: 48,
leading: const Icon(Symbols.rss_feed),
trailing: const Icon(Symbols.chevron_right),
contentPadding: EdgeInsets.symmetric(horizontal: 24),
title: Text('webFeeds').tr(),
onTap: () {
context.pushNamed('webFeedMarketplace');
},
),
ListTile(
minTileHeight: 48,
leading: const Icon(Symbols.star),
trailing: const Icon(Symbols.chevron_right),
contentPadding: EdgeInsets.symmetric(horizontal: 24),
title: Text('credits').tr(),
onTap: () {
context.pushNamed('socialCredits');
},
),
ListTile( ListTile(
minTileHeight: 48, minTileHeight: 48,
title: Text('abuseReport').tr(), title: Text('abuseReport').tr(),
@@ -389,6 +409,15 @@ class _UnauthorizedAccountScreen extends StatelessWidget {
}, },
child: Text('about').tr(), child: Text('about').tr(),
), ),
TextButton(
child: Text('debugOptions').tr(),
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) => DebugSheet(),
);
},
),
TextButton( TextButton(
onPressed: () { onPressed: () {
context.pushNamed('settings'); context.pushNamed('settings');

View File

@@ -0,0 +1,152 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart';
import 'package:island/pods/network.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
import 'package:styled_widget/styled_widget.dart';
part 'credits.g.dart';
@riverpod
Future<double> socialCredits(Ref ref) async {
final client = ref.watch(apiClientProvider);
final response = await client.get('/id/accounts/me/credits');
if (response.statusCode != 200) {
throw Exception('Failed to load social credits');
}
return response.data?.toDouble() ?? 0.0;
}
@riverpod
class SocialCreditHistoryNotifier extends _$SocialCreditHistoryNotifier
with CursorPagingNotifierMixin<SnSocialCreditRecord> {
static const int _pageSize = 20;
@override
Future<CursorPagingData<SnSocialCreditRecord>> build() => fetch(cursor: null);
@override
Future<CursorPagingData<SnSocialCreditRecord>> fetch({
required String? cursor,
}) async {
final client = ref.read(apiClientProvider);
final offset = cursor == null ? 0 : int.parse(cursor);
final queryParams = {'offset': offset, 'take': _pageSize};
final response = await client.get(
'/id/accounts/me/credits/history',
queryParameters: queryParams,
);
final total = int.parse(response.headers.value('X-Total') ?? '0');
final List<dynamic> data = response.data;
final records =
data.map((json) => SnSocialCreditRecord.fromJson(json)).toList();
final hasMore = offset + records.length < total;
final nextCursor = hasMore ? (offset + records.length).toString() : null;
return CursorPagingData(
items: records,
hasMore: hasMore,
nextCursor: nextCursor,
);
}
}
class SocialCreditsScreen extends HookConsumerWidget {
const SocialCreditsScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final socialCredits = ref.watch(socialCreditsProvider);
return AppScaffold(
appBar: AppBar(title: Text('socialCredits').tr()),
body: Column(
children: [
Card(
margin: EdgeInsets.only(left: 16, right: 16, top: 8),
child: socialCredits
.when(
data:
(credits) => Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
credits < 100
? 'socialCreditsLevelPoor'.tr()
: credits < 150
? 'socialCreditsLevelNormal'.tr()
: credits < 200
? 'socialCreditsLevelGood'.tr()
: 'socialCreditsLevelExcellent'.tr(),
).tr().bold().fontSize(20),
Text(
'${credits.toStringAsFixed(2)} pts',
).fontSize(14),
const Gap(8),
LinearProgressIndicator(value: credits / 200),
],
),
Positioned(
right: 0,
top: 0,
child: IconButton(
onPressed: () {},
icon: const Icon(Symbols.info),
tooltip: 'socialCreditsDescription'.tr(),
),
),
],
),
error: (_, _) => Text('Error loading credits'),
loading: () => const LinearProgressIndicator(),
)
.padding(horizontal: 20, vertical: 16),
),
Expanded(
child: PagingHelperView(
provider: socialCreditHistoryNotifierProvider,
futureRefreshable: socialCreditHistoryNotifierProvider.future,
notifierRefreshable: socialCreditHistoryNotifierProvider.notifier,
contentBuilder:
(data, widgetCount, endItemView) => ListView.builder(
padding: EdgeInsets.zero,
itemCount: widgetCount,
itemBuilder: (context, index) {
if (index == widgetCount - 1) {
return endItemView;
}
final record = data.items[index];
return ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 24),
title: Text(record.reason),
subtitle: Text(
DateFormat.yMMMd().format(record.createdAt),
),
trailing: Text(
record.delta > 0
? '+${record.delta}'
: '${record.delta}',
style: TextStyle(
color: record.delta > 0 ? Colors.green : Colors.red,
),
),
);
},
),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,49 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'credits.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$socialCreditsHash() => r'2599844e892127ee4d315caced5c10e4dbaea142';
/// See also [socialCredits].
@ProviderFor(socialCredits)
final socialCreditsProvider = AutoDisposeFutureProvider<double>.internal(
socialCredits,
name: r'socialCreditsProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$socialCreditsHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef SocialCreditsRef = AutoDisposeFutureProviderRef<double>;
String _$socialCreditHistoryNotifierHash() =>
r'950db020754160f835c64cedf3fa2175e61e4d64';
/// See also [SocialCreditHistoryNotifier].
@ProviderFor(SocialCreditHistoryNotifier)
final socialCreditHistoryNotifierProvider = AutoDisposeAsyncNotifierProvider<
SocialCreditHistoryNotifier,
CursorPagingData<SnSocialCreditRecord>
>.internal(
SocialCreditHistoryNotifier.new,
name: r'socialCreditHistoryNotifierProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$socialCreditHistoryNotifierHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$SocialCreditHistoryNotifier =
AutoDisposeAsyncNotifier<CursorPagingData<SnSocialCreditRecord>>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart';
import 'package:island/models/wallet.dart'; import 'package:island/models/wallet.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/pods/userinfo.dart';
@@ -19,6 +20,7 @@ import 'package:island/widgets/payment/payment_overlay.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
part 'leveling.g.dart'; part 'leveling.g.dart';
@@ -35,13 +37,49 @@ Future<SnWalletSubscription?> accountStellarSubscription(Ref ref) async {
} }
} }
@riverpod
class LevelingHistoryNotifier extends _$LevelingHistoryNotifier
with CursorPagingNotifierMixin<SnExperienceRecord> {
static const int _pageSize = 20;
@override
Future<CursorPagingData<SnExperienceRecord>> build() => fetch(cursor: null);
@override
Future<CursorPagingData<SnExperienceRecord>> fetch({
required String? cursor,
}) async {
final client = ref.read(apiClientProvider);
final offset = cursor == null ? 0 : int.parse(cursor);
final queryParams = {'offset': offset, 'take': _pageSize};
final response = await client.get(
'/id/accounts/me/leveling',
queryParameters: queryParams,
);
final total = int.parse(response.headers.value('X-Total') ?? '0');
final List<dynamic> data = response.data;
final records =
data.map((json) => SnExperienceRecord.fromJson(json)).toList();
final hasMore = offset + records.length < total;
final nextCursor = hasMore ? (offset + records.length).toString() : null;
return CursorPagingData(
items: records,
hasMore: hasMore,
nextCursor: nextCursor,
);
}
}
class LevelingScreen extends HookConsumerWidget { class LevelingScreen extends HookConsumerWidget {
const LevelingScreen({super.key}); const LevelingScreen({super.key});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userInfoProvider); final user = ref.watch(userInfoProvider);
final stellarSubscription = ref.watch(accountStellarSubscriptionProvider);
if (user.value == null) { if (user.value == null) {
return AppScaffold( return AppScaffold(
@@ -50,47 +88,150 @@ class LevelingScreen extends HookConsumerWidget {
); );
} }
final currentLevel = user.value!.profile.level; return DefaultTabController(
final currentExp = user.value!.profile.experience; length: 2,
final progress = user.value!.profile.levelingProgress; child: AppScaffold(
appBar: AppBar(
title: Text('levelingProgress'.tr()),
bottom: TabBar(
tabs: [
Tab(text: 'leveling'.tr()),
Tab(text: 'stellarProgram'.tr()),
],
),
),
body: TabBarView(
children: [
_buildLevelingTab(context, ref, user.value!),
_buildStellarProgramTab(context, ref),
],
),
),
);
}
return AppScaffold( Widget _buildLevelingTab(
appBar: AppBar(title: Text('levelingProgress'.tr())), BuildContext context,
body: SingleChildScrollView( WidgetRef ref,
padding: getTabbedPadding(context, horizontal: 20, vertical: 20), SnAccount user,
child: Center( ) {
child: ConstrainedBox( final currentLevel = user.profile.level;
constraints: const BoxConstraints(maxWidth: 480), final currentExp = user.profile.experience;
child: Column( final progress = user.profile.levelingProgress;
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Current Progress Card
LevelingProgressCard(
level: currentLevel,
experience: currentExp,
progress: progress,
),
const Gap(24),
// Level Stairs Graph return Center(
Text( child: Container(
'levelProgress'.tr(), padding: const EdgeInsets.symmetric(horizontal: 20),
style: Theme.of(context).textTheme.headlineSmall?.copyWith( constraints: const BoxConstraints(maxWidth: 480),
fontWeight: FontWeight.bold, child: CustomScrollView(
), slivers: [
), const SliverGap(20),
const Gap(16),
// Stairs visualization with fixed height and horizontal scroll // Current Progress Card
_buildLevelStairs(context, currentLevel), SliverToBoxAdapter(
child: LevelingProgressCard(
const Gap(24), level: currentLevel,
experience: currentExp,
// Membership section progress: progress,
_buildMembershipSection(context, ref, stellarSubscription), ),
const Gap(16),
],
), ),
const SliverGap(24),
// Level Stairs Graph
SliverToBoxAdapter(
child: Text(
'levelProgress'.tr(),
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
const SliverGap(16),
// Stairs visualization with fixed height and horizontal scroll
SliverToBoxAdapter(child: _buildLevelStairs(context, currentLevel)),
const SliverGap(24),
// Leveling History
SliverToBoxAdapter(
child: Text(
'levelingHistory'.tr(),
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
const SliverGap(8),
PagingHelperSliverView(
provider: levelingHistoryNotifierProvider,
futureRefreshable: levelingHistoryNotifierProvider.future,
notifierRefreshable: levelingHistoryNotifierProvider.notifier,
contentBuilder:
(data, widgetCount, endItemView) => SliverList.builder(
itemCount: widgetCount,
itemBuilder: (context, index) {
if (index == widgetCount - 1) {
return endItemView;
}
final record = data.items[index];
return ListTile(
title: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(record.reason),
Row(
spacing: 4,
children: [
Text(
record.createdAt.formatRelative(context),
).fontSize(13),
Text('·').fontSize(13).bold(),
Text(
record.createdAt.formatSystem(),
).fontSize(13),
],
).opacity(0.8),
],
),
subtitle: Row(
spacing: 8,
children: [
Text(
'${record.delta > 0 ? '+' : ''}${record.delta} EXP',
),
if (record.bonusMultiplier != 1.0)
Text('x${record.bonusMultiplier}'),
],
),
minTileHeight: 56,
contentPadding: EdgeInsets.symmetric(horizontal: 4),
);
},
),
),
SliverGap(getTabbedPadding(context, vertical: 20).vertical),
],
),
),
);
}
Widget _buildStellarProgramTab(BuildContext context, WidgetRef ref) {
final stellarSubscription = ref.watch(accountStellarSubscriptionProvider);
return SingleChildScrollView(
padding: getTabbedPadding(context, horizontal: 20, vertical: 20),
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 480),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildMembershipSection(context, ref, stellarSubscription),
const Gap(16),
],
), ),
), ),
), ),

View File

@@ -27,5 +27,26 @@ final accountStellarSubscriptionProvider =
// ignore: unused_element // ignore: unused_element
typedef AccountStellarSubscriptionRef = typedef AccountStellarSubscriptionRef =
AutoDisposeFutureProviderRef<SnWalletSubscription?>; AutoDisposeFutureProviderRef<SnWalletSubscription?>;
String _$levelingHistoryNotifierHash() =>
r'e795f9b7911c9e50f15c095ea237cb0e87bf1e89';
/// See also [LevelingHistoryNotifier].
@ProviderFor(LevelingHistoryNotifier)
final levelingHistoryNotifierProvider = AutoDisposeAsyncNotifierProvider<
LevelingHistoryNotifier,
CursorPagingData<SnExperienceRecord>
>.internal(
LevelingHistoryNotifier.new,
name: r'levelingHistoryNotifierProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$levelingHistoryNotifierHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$LevelingHistoryNotifier =
AutoDisposeAsyncNotifier<CursorPagingData<SnExperienceRecord>>;
// ignore_for_file: type=lint // ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -23,7 +23,7 @@ import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
part 'settings.g.dart'; part 'account_settings.g.dart';
@riverpod @riverpod
Future<List<SnAuthFactor>> authFactors(Ref ref) async { Future<List<SnAuthFactor>> authFactors(Ref ref) async {

View File

@@ -1,6 +1,6 @@
// GENERATED CODE - DO NOT MODIFY BY HAND // GENERATED CODE - DO NOT MODIFY BY HAND
part of 'settings.dart'; part of 'account_settings.dart';
// ************************************************************************** // **************************************************************************
// RiverpodGenerator // RiverpodGenerator

View File

@@ -1,3 +1,4 @@
import 'package:collection/collection.dart';
import 'package:croppy/croppy.dart' hide cropImage; import 'package:croppy/croppy.dart' hide cropImage;
import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
@@ -168,11 +169,18 @@ class UpdateProfileScreen extends HookConsumerWidget {
'location': locationController.text, 'location': locationController.text,
'time_zone': timeZoneController.text, 'time_zone': timeZoneController.text,
'birthday': birthday.value?.toUtc().toIso8601String(), 'birthday': birthday.value?.toUtc().toIso8601String(),
'links': links.value, 'links':
links.value
.where((e) => e.name.isNotEmpty && e.url.isNotEmpty)
.toList(),
}, },
); );
final userNotifier = ref.read(userInfoProvider.notifier); final userNotifier = ref.read(userInfoProvider.notifier);
userNotifier.fetchUser(); userNotifier.fetchUser();
links.value =
links.value
.where((e) => e.name.isNotEmpty && e.url.isNotEmpty)
.toList();
} catch (err) { } catch (err) {
showErrorAlert(err); showErrorAlert(err);
} finally { } finally {
@@ -568,6 +576,7 @@ class UpdateProfileScreen extends HookConsumerWidget {
children: [ children: [
for (var i = 0; i < links.value.length; i++) for (var i = 0; i < links.value.length; i++)
Row( Row(
key: ValueKey(links.value[i].hashCode),
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
Expanded( Expanded(
@@ -610,8 +619,10 @@ class UpdateProfileScreen extends HookConsumerWidget {
IconButton( IconButton(
icon: const Icon(Symbols.delete), icon: const Icon(Symbols.delete),
onPressed: () { onPressed: () {
links.value = List.from(links.value) links.value =
..removeAt(i); links.value
.whereIndexed((idx, _) => idx != i)
.toList();
}, },
), ),
], ],

View File

@@ -6,7 +6,7 @@ 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/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/screens/account/me/settings.dart'; import 'package:island/screens/account/me/account_settings.dart';
import 'package:island/screens/auth/oidc.native.dart'; import 'package:island/screens/auth/oidc.native.dart';
import 'package:island/services/text.dart'; import 'package:island/services/text.dart';
import 'package:island/services/time.dart'; import 'package:island/services/time.dart';

View File

@@ -7,6 +7,7 @@ import 'package:go_router/go_router.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/chat.dart'; import 'package:island/models/chat.dart';
import 'package:island/models/developer.dart';
import 'package:island/models/relationship.dart'; import 'package:island/models/relationship.dart';
import 'package:island/models/account.dart'; import 'package:island/models/account.dart';
import 'package:island/pods/config.dart'; import 'package:island/pods/config.dart';
@@ -112,6 +113,24 @@ Future<SnRelationship?> accountRelationship(Ref ref, String uname) async {
} }
} }
@riverpod
Future<SnDeveloper?> accountBotDeveloper(Ref ref, String uname) async {
final account = await ref.watch(accountProvider(uname).future);
if (account.automatedId == null) return null;
final apiClient = ref.watch(apiClientProvider);
try {
final resp = await apiClient.get(
"/develop/bots/${account.automatedId}/developer",
);
return SnDeveloper.fromJson(resp.data);
} catch (err) {
if (err is DioException && err.response?.statusCode == 404) {
return null;
}
rethrow;
}
}
class AccountProfileScreen extends HookConsumerWidget { class AccountProfileScreen extends HookConsumerWidget {
final String name; final String name;
const AccountProfileScreen({super.key, required this.name}); const AccountProfileScreen({super.key, required this.name});
@@ -128,6 +147,7 @@ class AccountProfileScreen extends HookConsumerWidget {
); );
final accountChat = ref.watch(accountDirectChatProvider(name)); final accountChat = ref.watch(accountDirectChatProvider(name));
final accountRelationship = ref.watch(accountRelationshipProvider(name)); final accountRelationship = ref.watch(accountRelationshipProvider(name));
final accountDeveloper = ref.watch(accountBotDeveloperProvider(name));
final appbarColor = ref.watch(accountAppbarForcegroundColorProvider(name)); final appbarColor = ref.watch(accountAppbarForcegroundColorProvider(name));
@@ -292,6 +312,19 @@ class AccountProfileScreen extends HookConsumerWidget {
), ),
], ],
), ),
if (accountDeveloper.value != null)
Row(
spacing: 7,
children: [
const Icon(Symbols.smart_toy, size: 18),
Text(
'botAutomatedBy'.tr(
args: [accountDeveloper.value!.publisher!.nick],
),
).fontSize(13),
],
).opacity(0.75),
const Gap(4),
AccountStatusWidget(uname: name, padding: EdgeInsets.zero), AccountStatusWidget(uname: name, padding: EdgeInsets.zero),
], ],
), ),

View File

@@ -639,5 +639,128 @@ class _AccountRelationshipProviderElement
String get uname => (origin as AccountRelationshipProvider).uname; String get uname => (origin as AccountRelationshipProvider).uname;
} }
String _$accountBotDeveloperHash() =>
r'673534770640a8cf1484ea0af0f4d0ef283ef157';
/// See also [accountBotDeveloper].
@ProviderFor(accountBotDeveloper)
const accountBotDeveloperProvider = AccountBotDeveloperFamily();
/// See also [accountBotDeveloper].
class AccountBotDeveloperFamily extends Family<AsyncValue<SnDeveloper?>> {
/// See also [accountBotDeveloper].
const AccountBotDeveloperFamily();
/// See also [accountBotDeveloper].
AccountBotDeveloperProvider call(String uname) {
return AccountBotDeveloperProvider(uname);
}
@override
AccountBotDeveloperProvider getProviderOverride(
covariant AccountBotDeveloperProvider provider,
) {
return call(provider.uname);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'accountBotDeveloperProvider';
}
/// See also [accountBotDeveloper].
class AccountBotDeveloperProvider
extends AutoDisposeFutureProvider<SnDeveloper?> {
/// See also [accountBotDeveloper].
AccountBotDeveloperProvider(String uname)
: this._internal(
(ref) => accountBotDeveloper(ref as AccountBotDeveloperRef, uname),
from: accountBotDeveloperProvider,
name: r'accountBotDeveloperProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$accountBotDeveloperHash,
dependencies: AccountBotDeveloperFamily._dependencies,
allTransitiveDependencies:
AccountBotDeveloperFamily._allTransitiveDependencies,
uname: uname,
);
AccountBotDeveloperProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.uname,
}) : super.internal();
final String uname;
@override
Override overrideWith(
FutureOr<SnDeveloper?> Function(AccountBotDeveloperRef provider) create,
) {
return ProviderOverride(
origin: this,
override: AccountBotDeveloperProvider._internal(
(ref) => create(ref as AccountBotDeveloperRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
uname: uname,
),
);
}
@override
AutoDisposeFutureProviderElement<SnDeveloper?> createElement() {
return _AccountBotDeveloperProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is AccountBotDeveloperProvider && other.uname == uname;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, uname.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin AccountBotDeveloperRef on AutoDisposeFutureProviderRef<SnDeveloper?> {
/// The parameter `uname` of this provider.
String get uname;
}
class _AccountBotDeveloperProviderElement
extends AutoDisposeFutureProviderElement<SnDeveloper?>
with AccountBotDeveloperRef {
_AccountBotDeveloperProviderElement(super.provider);
@override
String get uname => (origin as AccountBotDeveloperProvider).uname;
}
// ignore_for_file: type=lint // ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -6,7 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/screens/account/me/update.dart'; import 'package:island/screens/account/me/profile_update.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';

View File

@@ -23,7 +23,7 @@ import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/widgets/content/sheet.dart';
import 'package:island/widgets/realms/selection_dropdown.dart'; import 'package:island/widgets/realm/realm_selection_dropdown.dart';
import 'package:island/widgets/response.dart'; import 'package:island/widgets/response.dart';
import 'package:island/screens/tabs.dart'; import 'package:island/screens/tabs.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
@@ -79,31 +79,38 @@ class ChatRoomListTile extends HookConsumerWidget {
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
), ),
), ),
Row( if (data.lastMessage == null)
children: [ Text(room.description ?? 'descriptionNone'.tr(), maxLines: 1)
Text( else
'${data.lastMessage.sender.account.name}: ', Row(
style: Theme.of(context).textTheme.bodySmall, spacing: 4,
), children: [
Expanded( Badge(
child: Text( label: Text(data.lastMessage!.sender.account.nick),
(data.lastMessage.content?.isNotEmpty ?? false) textColor: Theme.of(context).colorScheme.onPrimary,
? data.lastMessage.content! backgroundColor: Theme.of(context).colorScheme.primary,
: 'messageNone'.tr(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodySmall,
), ),
), Expanded(
Align( child: Text(
alignment: Alignment.centerRight, (data.lastMessage!.content?.isNotEmpty ?? false)
child: Text( ? data.lastMessage!.content!
RelativeTime(context).format(data.lastMessage.createdAt), : 'messageNone'.tr(),
style: Theme.of(context).textTheme.bodySmall, maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodySmall,
),
), ),
), Align(
], alignment: Alignment.centerRight,
), child: Text(
RelativeTime(
context,
).format(data.lastMessage!.createdAt),
style: Theme.of(context).textTheme.bodySmall,
),
),
],
),
], ],
); );
}, },

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ part of 'room.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$messagesNotifierHash() => r'afc4d43f4948ec571118cef0321838a6cefc89c0'; String _$messagesNotifierHash() => r'32afe6ea24086d869cc47bd3389c8fd734409ca0';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {

View File

@@ -10,6 +10,7 @@ import 'package:island/models/chat.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/screens/chat/chat.dart'; import 'package:island/screens/chat/chat.dart';
import 'package:island/widgets/account/account_picker.dart'; import 'package:island/widgets/account/account_picker.dart';
import 'package:island/widgets/account/status.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
@@ -544,7 +545,7 @@ class ChatMemberListNotifier extends _$ChatMemberListNotifier
final apiClient = ref.watch(apiClientProvider); final apiClient = ref.watch(apiClientProvider);
final response = await apiClient.get( final response = await apiClient.get(
'/sphere/chat/$roomId/members', '/sphere/chat/$roomId/members',
queryParameters: {'offset': offset, 'take': take}, queryParameters: {'offset': offset, 'take': take, 'withStatus': true},
); );
final total = int.parse(response.headers.value('X-Total') ?? '0'); final total = int.parse(response.headers.value('X-Total') ?? '0');
@@ -672,6 +673,8 @@ class _ChatMemberListSheet extends HookConsumerWidget {
spacing: 6, spacing: 6,
children: [ children: [
Flexible(child: Text(member.account.nick)), Flexible(child: Text(member.account.nick)),
if (member.status != null)
AccountStatusLabel(status: member.status!),
if (member.joinedAt == null) if (member.joinedAt == null)
const Icon(Symbols.pending_actions, size: 20), const Icon(Symbols.pending_actions, size: 20),
], ],

View File

@@ -7,7 +7,7 @@ part of 'room_detail.dart';
// ************************************************************************** // **************************************************************************
String _$chatMemberListNotifierHash() => String _$chatMemberListNotifierHash() =>
r'c8fbf4b95df6dae24b1ba21063e9a43351832974'; r'3ea30150278523e9f6b23f9200ea9a9fbae9c973';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {

View File

@@ -212,30 +212,6 @@ class CreatorHubScreen extends HookConsumerWidget {
leading: !isWide ? const PageBackButton() : null, leading: !isWide ? const PageBackButton() : null,
title: Text('creatorHub').tr(), title: Text('creatorHub').tr(),
actions: [ actions: [
IconButton(
icon: Badge(
label: Text(
publisherInvites.when(
data: (invites) => invites.length.toString(),
error: (_, _) => '0',
loading: () => '0',
),
),
isLabelVisible: publisherInvites.when(
data: (invites) => invites.isNotEmpty,
error: (_, _) => false,
loading: () => false,
),
child: const Icon(Symbols.email),
),
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (_) => const _PublisherInviteSheet(),
);
},
),
DropdownButtonHideUnderline( DropdownButtonHideUnderline(
child: DropdownButton2<SnPublisher>( child: DropdownButton2<SnPublisher>(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
@@ -323,6 +299,23 @@ class CreatorHubScreen extends HookConsumerWidget {
), ),
) ?? ) ??
[]), []),
ListTile(
leading: const CircleAvatar(
child: Icon(Symbols.mail),
),
title: Text('publisherCollabInvitation').tr(),
subtitle: Text(
'publisherCollabInvitationCount',
).plural(publisherInvites.value?.length ?? 0),
trailing: const Icon(Symbols.chevron_right),
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (_) => const _PublisherInviteSheet(),
);
},
),
ListTile( ListTile(
leading: const CircleAvatar( leading: const CircleAvatar(
child: Icon(Symbols.add), child: Icon(Symbols.add),

View File

@@ -19,7 +19,7 @@ import 'package:island/services/responsive.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/realms/selection_dropdown.dart'; import 'package:island/widgets/realm/realm_selection_dropdown.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';

View File

@@ -106,11 +106,7 @@ class StickerPacksNotifier extends _$StickerPacksNotifier
try { try {
final response = await client.get( final response = await client.get(
'/sphere/stickers', '/sphere/stickers',
queryParameters: { queryParameters: {'offset': offset, 'take': _pageSize, 'pub': pubName},
'offset': offset,
'take': _pageSize,
'pubName': pubName,
},
); );
final total = int.parse(response.headers.value('X-Total') ?? '0'); final total = int.parse(response.headers.value('X-Total') ?? '0');

View File

@@ -148,7 +148,7 @@ class _StickerPackProviderElement
} }
String _$stickerPacksNotifierHash() => String _$stickerPacksNotifierHash() =>
r'0a8edcf9c35396c411f1214f5e77b1e8fac6a3e6'; r'30024b35235f3085a5b1ec2204d0a974ee907e22';
abstract class _$StickerPacksNotifier abstract class _$StickerPacksNotifier
extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnStickerPack>> { extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnStickerPack>> {

View File

@@ -0,0 +1,131 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/custom_app.dart';
import 'package:island/screens/developers/app_secrets.dart';
import 'package:island/screens/developers/apps.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart';
class AppDetailScreen extends HookConsumerWidget {
final String publisherName;
final String projectId;
final String appId;
const AppDetailScreen({
super.key,
required this.publisherName,
required this.projectId,
required this.appId,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final tabController = useTabController(initialLength: 2);
final appData = ref.watch(customAppProvider(publisherName, projectId, appId));
return AppScaffold(
appBar: AppBar(
title: Text(appData.value?.name ?? 'appDetails'.tr()),
actions: [
IconButton(
icon: const Icon(Symbols.edit),
onPressed: appData.value == null
? null
: () {
context.pushNamed(
'developerAppEdit',
pathParameters: {
'name': publisherName,
'projectId': projectId,
'id': appId,
},
);
},
),
],
bottom: TabBar(
controller: tabController,
tabs: [Tab(text: 'overview'.tr()), Tab(text: 'secrets'.tr())],
),
),
body: appData.when(
data: (app) {
return TabBarView(
controller: tabController,
physics: const NeverScrollableScrollPhysics(),
children: [
_AppOverview(app: app),
AppSecretsScreen(
publisherName: publisherName,
projectId: projectId,
appId: appId,
),
],
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error: (err, stack) => ResponseErrorWidget(
error: err,
onRetry: () => ref.invalidate(
customAppProvider(publisherName, projectId, appId),
),
),
),
);
}
}
class _AppOverview extends StatelessWidget {
final CustomApp app;
const _AppOverview({required this.app});
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: [
AspectRatio(
aspectRatio: 16 / 7,
child: Stack(
clipBehavior: Clip.none,
fit: StackFit.expand,
children: [
Container(
color: Theme.of(context).colorScheme.surfaceContainer,
child: app.background != null
? CloudFileWidget(
item: app.background!,
fit: BoxFit.cover,
)
: const SizedBox.shrink(),
),
Positioned(
left: 20,
bottom: -32,
child: ProfilePictureWidget(
fileId: app.picture?.id,
radius: 40,
fallbackIcon: Symbols.apps,
),
),
],
),
).padding(bottom: 32),
ListTile(title: Text('name'.tr()), subtitle: Text(app.name)),
ListTile(title: Text('slug'.tr()), subtitle: Text(app.slug)),
if (app.description?.isNotEmpty ?? false)
ListTile(
title: Text('description'.tr()),
subtitle: Text(app.description!),
),
],
).padding(bottom: 24),
);
}
}

View File

@@ -0,0 +1,252 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/custom_app_secret.dart';
import 'package:island/pods/network.dart';
import 'package:island/services/time.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/content/sheet.dart';
import 'package:island/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'app_secrets.g.dart';
@riverpod
Future<List<CustomAppSecret>> customAppSecrets(
Ref ref,
String publisherName,
String projectId,
String appId,
) async {
final client = ref.watch(apiClientProvider);
final resp = await client.get(
'/develop/developers/$publisherName/projects/$projectId/apps/$appId/secrets',
);
return (resp.data as List)
.map((e) => CustomAppSecret.fromJson(e))
.cast<CustomAppSecret>()
.toList();
}
class AppSecretsScreen extends HookConsumerWidget {
final String publisherName;
final String projectId;
final String appId;
const AppSecretsScreen({
super.key,
required this.publisherName,
required this.projectId,
required this.appId,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final secrets = ref.watch(
customAppSecretsProvider(publisherName, projectId, appId),
);
void showNewSecretSheet(String newSecret) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder:
(context) => SheetScaffold(
titleText: 'newSecretGenerated'.tr(),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
Text('copySecretHint'.tr()),
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(8),
),
child: SelectableText(newSecret),
),
const SizedBox(height: 20),
FilledButton.icon(
onPressed: () {
Clipboard.setData(ClipboardData(text: newSecret));
},
icon: const Icon(Symbols.copy_all),
label: Text('copy'.tr()),
),
],
),
),
),
).whenComplete(() {
ref.invalidate(
customAppSecretsProvider(publisherName, projectId, appId),
);
});
}
void createSecret() {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) {
return HookBuilder(
builder: (context) {
final descriptionController = useTextEditingController();
final expiresInController = useTextEditingController();
final isOidc = useState(false);
return SheetScaffold(
titleText: 'generateSecret'.tr(),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: descriptionController,
decoration: InputDecoration(
labelText: 'description'.tr(),
),
autofocus: true,
),
const SizedBox(height: 20),
TextFormField(
controller: expiresInController,
decoration: InputDecoration(
labelText: 'expiresIn'.tr(),
),
keyboardType: TextInputType.number,
),
const SizedBox(height: 20),
SwitchListTile(
title: Text('isOidc'.tr()),
value: isOidc.value,
onChanged: (value) => isOidc.value = value,
),
const SizedBox(height: 20),
FilledButton.icon(
onPressed: () async {
final description = descriptionController.text;
final expiresIn = int.tryParse(
expiresInController.text,
);
Navigator.pop(context); // Close the sheet
try {
final client = ref.read(apiClientProvider);
final resp = await client.post(
'/develop/developers/$publisherName/projects/$projectId/apps/$appId/secrets',
data: {
'description': description,
'expires_in': expiresIn,
'is_oidc': isOidc.value,
},
);
final newSecret = CustomAppSecret.fromJson(
resp.data,
);
if (newSecret.secret != null) {
showNewSecretSheet(newSecret.secret!);
}
} catch (e) {
showErrorAlert(e.toString());
}
},
icon: const Icon(Symbols.add),
label: Text('create'.tr()),
),
],
),
),
);
},
);
},
);
}
return secrets.when(
data: (data) {
return RefreshIndicator(
onRefresh:
() => ref.refresh(
customAppSecretsProvider(
publisherName,
projectId,
appId,
).future,
),
child: Column(
children: [
ListTile(
leading: const Icon(Symbols.add),
title: Text('generateSecret'.tr()),
onTap: createSecret,
),
const Divider(height: 1),
Expanded(
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: data.length,
itemBuilder: (context, index) {
final secret = data[index];
return ListTile(
title: Text(secret.description ?? secret.id),
subtitle: Text(
'createdAt'.tr(args: [secret.createdAt.formatSystem()]),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Symbols.delete, color: Colors.red),
onPressed: () {
showConfirmAlert(
'deleteSecretHint'.tr(),
'deleteSecret'.tr(),
).then((confirm) {
if (confirm) {
final client = ref.read(apiClientProvider);
client.delete(
'/develop/developers/$publisherName/projects/$projectId/apps/$appId/secrets/${secret.id}',
);
ref.invalidate(
customAppSecretsProvider(
publisherName,
projectId,
appId,
),
);
}
});
},
),
],
),
);
},
),
),
],
),
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error:
(err, stack) => ResponseErrorWidget(
error: err,
onRetry:
() => ref.invalidate(
customAppSecretsProvider(publisherName, projectId, appId),
),
),
);
}
}

View File

@@ -0,0 +1,188 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'app_secrets.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$customAppSecretsHash() => r'1bc62ad812487883ce739793b22a76168d656752';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [customAppSecrets].
@ProviderFor(customAppSecrets)
const customAppSecretsProvider = CustomAppSecretsFamily();
/// See also [customAppSecrets].
class CustomAppSecretsFamily extends Family<AsyncValue<List<CustomAppSecret>>> {
/// See also [customAppSecrets].
const CustomAppSecretsFamily();
/// See also [customAppSecrets].
CustomAppSecretsProvider call(
String publisherName,
String projectId,
String appId,
) {
return CustomAppSecretsProvider(publisherName, projectId, appId);
}
@override
CustomAppSecretsProvider getProviderOverride(
covariant CustomAppSecretsProvider provider,
) {
return call(provider.publisherName, provider.projectId, provider.appId);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'customAppSecretsProvider';
}
/// See also [customAppSecrets].
class CustomAppSecretsProvider
extends AutoDisposeFutureProvider<List<CustomAppSecret>> {
/// See also [customAppSecrets].
CustomAppSecretsProvider(String publisherName, String projectId, String appId)
: this._internal(
(ref) => customAppSecrets(
ref as CustomAppSecretsRef,
publisherName,
projectId,
appId,
),
from: customAppSecretsProvider,
name: r'customAppSecretsProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$customAppSecretsHash,
dependencies: CustomAppSecretsFamily._dependencies,
allTransitiveDependencies:
CustomAppSecretsFamily._allTransitiveDependencies,
publisherName: publisherName,
projectId: projectId,
appId: appId,
);
CustomAppSecretsProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.publisherName,
required this.projectId,
required this.appId,
}) : super.internal();
final String publisherName;
final String projectId;
final String appId;
@override
Override overrideWith(
FutureOr<List<CustomAppSecret>> Function(CustomAppSecretsRef provider)
create,
) {
return ProviderOverride(
origin: this,
override: CustomAppSecretsProvider._internal(
(ref) => create(ref as CustomAppSecretsRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
publisherName: publisherName,
projectId: projectId,
appId: appId,
),
);
}
@override
AutoDisposeFutureProviderElement<List<CustomAppSecret>> createElement() {
return _CustomAppSecretsProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is CustomAppSecretsProvider &&
other.publisherName == publisherName &&
other.projectId == projectId &&
other.appId == appId;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, publisherName.hashCode);
hash = _SystemHash.combine(hash, projectId.hashCode);
hash = _SystemHash.combine(hash, appId.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin CustomAppSecretsRef
on AutoDisposeFutureProviderRef<List<CustomAppSecret>> {
/// The parameter `publisherName` of this provider.
String get publisherName;
/// The parameter `projectId` of this provider.
String get projectId;
/// The parameter `appId` of this provider.
String get appId;
}
class _CustomAppSecretsProviderElement
extends AutoDisposeFutureProviderElement<List<CustomAppSecret>>
with CustomAppSecretsRef {
_CustomAppSecretsProviderElement(super.provider);
@override
String get publisherName =>
(origin as CustomAppSecretsProvider).publisherName;
@override
String get projectId => (origin as CustomAppSecretsProvider).projectId;
@override
String get appId => (origin as CustomAppSecretsProvider).appId;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -6,7 +6,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/custom_app.dart'; import 'package:island/models/custom_app.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/response.dart'; import 'package:island/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
@@ -16,50 +15,98 @@ import 'package:styled_widget/styled_widget.dart';
part 'apps.g.dart'; part 'apps.g.dart';
@riverpod @riverpod
Future<List<CustomApp>> customApps(Ref ref, String publisherName) async { Future<CustomApp> customApp(
Ref ref,
String publisherName,
String projectId,
String appId,
) async {
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
final resp = await client.get('/develop/developers/$publisherName/apps'); final resp = await client.get(
return resp.data.map((e) => CustomApp.fromJson(e)).cast<CustomApp>().toList(); '/develop/developers/$publisherName/projects/$projectId/apps/$appId',
);
return CustomApp.fromJson(resp.data);
}
@riverpod
Future<List<CustomApp>> customApps(
Ref ref,
String publisherName,
String projectId,
) async {
final client = ref.watch(apiClientProvider);
final resp = await client.get(
'/develop/developers/$publisherName/projects/$projectId/apps',
);
return (resp.data as List)
.map((e) => CustomApp.fromJson(e))
.cast<CustomApp>()
.toList();
} }
class CustomAppsScreen extends HookConsumerWidget { class CustomAppsScreen extends HookConsumerWidget {
final String publisherName; final String publisherName;
const CustomAppsScreen({super.key, required this.publisherName}); final String projectId;
const CustomAppsScreen({
super.key,
required this.publisherName,
required this.projectId,
});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final apps = ref.watch(customAppsProvider(publisherName)); final apps = ref.watch(customAppsProvider(publisherName, projectId));
return AppScaffold( return apps.when(
appBar: AppBar( data: (data) {
title: Text('customApps').tr(), if (data.isEmpty) {
actions: [ return Center(
IconButton( child: Column(
icon: const Icon(Symbols.add), mainAxisAlignment: MainAxisAlignment.center,
onPressed: () { children: [
context.pushNamed( Text('noCustomApps').tr(),
'developerAppNew', const SizedBox(height: 16),
pathParameters: {'name': publisherName}, ElevatedButton.icon(
); onPressed: () {
}, context.pushNamed(
), 'developerAppNew',
], pathParameters: {
), 'name': publisherName,
body: apps.when( 'projectId': projectId,
data: (data) { },
if (data.isEmpty) { );
return Center(child: Text('noCustomApps').tr()); },
} icon: const Icon(Symbols.add),
return RefreshIndicator( label: Text('createCustomApp').tr(),
onRefresh: ),
() => ref.refresh(customAppsProvider(publisherName).future), ],
child: ListView.builder( ),
padding: EdgeInsets.only(top: 4), );
itemCount: data.length, }
itemBuilder: (context, index) { return RefreshIndicator(
final app = data[index]; onRefresh:
return Card( () => ref.refresh(
margin: const EdgeInsets.all(8.0), customAppsProvider(publisherName, projectId).future,
),
child: ListView.builder(
padding: EdgeInsets.only(top: 4),
itemCount: data.length,
itemBuilder: (context, index) {
final app = data[index];
return Card(
margin: const EdgeInsets.all(8.0),
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: () {
context.pushNamed(
'developerAppDetail',
pathParameters: {
'name': publisherName,
'projectId': projectId,
'appId': app.id,
},
);
},
child: Column( child: Column(
children: [ children: [
SizedBox( SizedBox(
@@ -128,6 +175,7 @@ class CustomAppsScreen extends HookConsumerWidget {
'developerAppEdit', 'developerAppEdit',
pathParameters: { pathParameters: {
'name': publisherName, 'name': publisherName,
'projectId': projectId,
'id': app.id, 'id': app.id,
}, },
); );
@@ -139,10 +187,13 @@ class CustomAppsScreen extends HookConsumerWidget {
if (confirm) { if (confirm) {
final client = ref.read(apiClientProvider); final client = ref.read(apiClientProvider);
client.delete( client.delete(
'/develop/developers/$publisherName/apps/${app.id}', '/develop/developers/$publisherName/projects/$projectId/apps/${app.id}',
); );
ref.invalidate( ref.invalidate(
customAppsProvider(publisherName), customAppsProvider(
publisherName,
projectId,
),
); );
} }
}); });
@@ -152,18 +203,21 @@ class CustomAppsScreen extends HookConsumerWidget {
), ),
], ],
), ),
); ),
}, );
), },
); ),
}, );
loading: () => const Center(child: CircularProgressIndicator()), },
error: loading: () => const Center(child: CircularProgressIndicator()),
(err, stack) => ResponseErrorWidget( error:
error: err, (err, stack) => ResponseErrorWidget(
onRetry: () => ref.invalidate(customAppsProvider(publisherName)), error: err,
), onRetry:
), () => ref.invalidate(
customAppsProvider(publisherName, projectId),
),
),
); );
} }
} }

View File

@@ -6,7 +6,7 @@ part of 'apps.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$customAppsHash() => r'c6ac78060eb51a2b208a749a81ecbe0a9c608ce1'; String _$customAppHash() => r'be05431ba8bf06fd20ee988a61c3663a68e15fc9';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {
@@ -29,6 +29,148 @@ class _SystemHash {
} }
} }
/// See also [customApp].
@ProviderFor(customApp)
const customAppProvider = CustomAppFamily();
/// See also [customApp].
class CustomAppFamily extends Family<AsyncValue<CustomApp>> {
/// See also [customApp].
const CustomAppFamily();
/// See also [customApp].
CustomAppProvider call(String publisherName, String projectId, String appId) {
return CustomAppProvider(publisherName, projectId, appId);
}
@override
CustomAppProvider getProviderOverride(covariant CustomAppProvider provider) {
return call(provider.publisherName, provider.projectId, provider.appId);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'customAppProvider';
}
/// See also [customApp].
class CustomAppProvider extends AutoDisposeFutureProvider<CustomApp> {
/// See also [customApp].
CustomAppProvider(String publisherName, String projectId, String appId)
: this._internal(
(ref) =>
customApp(ref as CustomAppRef, publisherName, projectId, appId),
from: customAppProvider,
name: r'customAppProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$customAppHash,
dependencies: CustomAppFamily._dependencies,
allTransitiveDependencies: CustomAppFamily._allTransitiveDependencies,
publisherName: publisherName,
projectId: projectId,
appId: appId,
);
CustomAppProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.publisherName,
required this.projectId,
required this.appId,
}) : super.internal();
final String publisherName;
final String projectId;
final String appId;
@override
Override overrideWith(
FutureOr<CustomApp> Function(CustomAppRef provider) create,
) {
return ProviderOverride(
origin: this,
override: CustomAppProvider._internal(
(ref) => create(ref as CustomAppRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
publisherName: publisherName,
projectId: projectId,
appId: appId,
),
);
}
@override
AutoDisposeFutureProviderElement<CustomApp> createElement() {
return _CustomAppProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is CustomAppProvider &&
other.publisherName == publisherName &&
other.projectId == projectId &&
other.appId == appId;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, publisherName.hashCode);
hash = _SystemHash.combine(hash, projectId.hashCode);
hash = _SystemHash.combine(hash, appId.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin CustomAppRef on AutoDisposeFutureProviderRef<CustomApp> {
/// The parameter `publisherName` of this provider.
String get publisherName;
/// The parameter `projectId` of this provider.
String get projectId;
/// The parameter `appId` of this provider.
String get appId;
}
class _CustomAppProviderElement
extends AutoDisposeFutureProviderElement<CustomApp>
with CustomAppRef {
_CustomAppProviderElement(super.provider);
@override
String get publisherName => (origin as CustomAppProvider).publisherName;
@override
String get projectId => (origin as CustomAppProvider).projectId;
@override
String get appId => (origin as CustomAppProvider).appId;
}
String _$customAppsHash() => r'450bedaf4220b8963cb44afeb14d4c0e80f01b11';
/// See also [customApps]. /// See also [customApps].
@ProviderFor(customApps) @ProviderFor(customApps)
const customAppsProvider = CustomAppsFamily(); const customAppsProvider = CustomAppsFamily();
@@ -39,15 +181,15 @@ class CustomAppsFamily extends Family<AsyncValue<List<CustomApp>>> {
const CustomAppsFamily(); const CustomAppsFamily();
/// See also [customApps]. /// See also [customApps].
CustomAppsProvider call(String publisherName) { CustomAppsProvider call(String publisherName, String projectId) {
return CustomAppsProvider(publisherName); return CustomAppsProvider(publisherName, projectId);
} }
@override @override
CustomAppsProvider getProviderOverride( CustomAppsProvider getProviderOverride(
covariant CustomAppsProvider provider, covariant CustomAppsProvider provider,
) { ) {
return call(provider.publisherName); return call(provider.publisherName, provider.projectId);
} }
static const Iterable<ProviderOrFamily>? _dependencies = null; static const Iterable<ProviderOrFamily>? _dependencies = null;
@@ -68,9 +210,9 @@ class CustomAppsFamily extends Family<AsyncValue<List<CustomApp>>> {
/// See also [customApps]. /// See also [customApps].
class CustomAppsProvider extends AutoDisposeFutureProvider<List<CustomApp>> { class CustomAppsProvider extends AutoDisposeFutureProvider<List<CustomApp>> {
/// See also [customApps]. /// See also [customApps].
CustomAppsProvider(String publisherName) CustomAppsProvider(String publisherName, String projectId)
: this._internal( : this._internal(
(ref) => customApps(ref as CustomAppsRef, publisherName), (ref) => customApps(ref as CustomAppsRef, publisherName, projectId),
from: customAppsProvider, from: customAppsProvider,
name: r'customAppsProvider', name: r'customAppsProvider',
debugGetCreateSourceHash: debugGetCreateSourceHash:
@@ -80,6 +222,7 @@ class CustomAppsProvider extends AutoDisposeFutureProvider<List<CustomApp>> {
dependencies: CustomAppsFamily._dependencies, dependencies: CustomAppsFamily._dependencies,
allTransitiveDependencies: CustomAppsFamily._allTransitiveDependencies, allTransitiveDependencies: CustomAppsFamily._allTransitiveDependencies,
publisherName: publisherName, publisherName: publisherName,
projectId: projectId,
); );
CustomAppsProvider._internal( CustomAppsProvider._internal(
@@ -90,9 +233,11 @@ class CustomAppsProvider extends AutoDisposeFutureProvider<List<CustomApp>> {
required super.debugGetCreateSourceHash, required super.debugGetCreateSourceHash,
required super.from, required super.from,
required this.publisherName, required this.publisherName,
required this.projectId,
}) : super.internal(); }) : super.internal();
final String publisherName; final String publisherName;
final String projectId;
@override @override
Override overrideWith( Override overrideWith(
@@ -108,6 +253,7 @@ class CustomAppsProvider extends AutoDisposeFutureProvider<List<CustomApp>> {
allTransitiveDependencies: null, allTransitiveDependencies: null,
debugGetCreateSourceHash: null, debugGetCreateSourceHash: null,
publisherName: publisherName, publisherName: publisherName,
projectId: projectId,
), ),
); );
} }
@@ -119,13 +265,16 @@ class CustomAppsProvider extends AutoDisposeFutureProvider<List<CustomApp>> {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return other is CustomAppsProvider && other.publisherName == publisherName; return other is CustomAppsProvider &&
other.publisherName == publisherName &&
other.projectId == projectId;
} }
@override @override
int get hashCode { int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode); var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, publisherName.hashCode); hash = _SystemHash.combine(hash, publisherName.hashCode);
hash = _SystemHash.combine(hash, projectId.hashCode);
return _SystemHash.finish(hash); return _SystemHash.finish(hash);
} }
@@ -136,6 +285,9 @@ class CustomAppsProvider extends AutoDisposeFutureProvider<List<CustomApp>> {
mixin CustomAppsRef on AutoDisposeFutureProviderRef<List<CustomApp>> { mixin CustomAppsRef on AutoDisposeFutureProviderRef<List<CustomApp>> {
/// The parameter `publisherName` of this provider. /// The parameter `publisherName` of this provider.
String get publisherName; String get publisherName;
/// The parameter `projectId` of this provider.
String get projectId;
} }
class _CustomAppsProviderElement class _CustomAppsProviderElement
@@ -145,6 +297,8 @@ class _CustomAppsProviderElement
@override @override
String get publisherName => (origin as CustomAppsProvider).publisherName; String get publisherName => (origin as CustomAppsProvider).publisherName;
@override
String get projectId => (origin as CustomAppsProvider).projectId;
} }
// ignore_for_file: type=lint // ignore_for_file: type=lint

View File

@@ -0,0 +1,142 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/bot.dart';
import 'package:island/screens/developers/bot_keys.dart';
import 'package:island/screens/developers/edit_bot.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:go_router/go_router.dart';
import 'package:styled_widget/styled_widget.dart';
class BotDetailScreen extends HookConsumerWidget {
final String publisherName;
final String projectId;
final String botId;
const BotDetailScreen({
super.key,
required this.publisherName,
required this.projectId,
required this.botId,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final tabController = useTabController(initialLength: 2);
final botData = ref.watch(botProvider(publisherName, projectId, botId));
return AppScaffold(
appBar: AppBar(
title: Text(botData.value?.account.nick ?? 'botDetails'.tr()),
actions: [
IconButton(
icon: const Icon(Symbols.edit),
onPressed:
botData.value == null
? null
: () {
context.pushNamed(
'developerBotEdit',
pathParameters: {
'name': publisherName,
'projectId': projectId,
'id': botId,
},
);
},
),
],
bottom: TabBar(
controller: tabController,
tabs: [Tab(text: 'overview'.tr()), Tab(text: 'keys'.tr())],
),
),
body: botData.when(
data: (bot) {
if (bot == null) {
return Center(child: Text('botNotFound'.tr()));
}
return TabBarView(
controller: tabController,
physics: const NeverScrollableScrollPhysics(),
children: [
_BotOverview(bot: bot),
BotKeysScreen(
publisherName: publisherName,
projectId: projectId,
botId: botId,
),
],
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error:
(err, stack) => ResponseErrorWidget(
error: err,
onRetry:
() => ref.invalidate(
botProvider(publisherName, projectId, botId),
),
),
),
);
}
}
class _BotOverview extends StatelessWidget {
final Bot bot;
const _BotOverview({required this.bot});
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: [
AspectRatio(
aspectRatio: 16 / 7,
child: Stack(
clipBehavior: Clip.none,
fit: StackFit.expand,
children: [
Container(
color: Theme.of(context).colorScheme.surfaceContainer,
child:
bot.account.profile.background != null
? CloudFileWidget(
item: bot.account.profile.background!,
fit: BoxFit.cover,
)
: const SizedBox.shrink(),
),
Positioned(
left: 20,
bottom: -32,
child: ProfilePictureWidget(
fileId: bot.account.profile.picture?.id,
radius: 40,
fallbackIcon: Symbols.smart_toy,
),
),
],
),
).padding(bottom: 32),
ListTile(title: Text('name'.tr()), subtitle: Text(bot.account.name)),
ListTile(
title: Text('nickname'.tr()),
subtitle: Text(bot.account.nick),
),
ListTile(title: Text('slug'.tr()), subtitle: Text(bot.slug)),
if (bot.account.profile.bio.isNotEmpty)
ListTile(
title: Text('bio'.tr()),
subtitle: Text(bot.account.profile.bio),
),
],
).padding(bottom: 24),
);
}
}

View File

@@ -0,0 +1,278 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/bot_key.dart';
import 'package:island/pods/network.dart';
import 'package:island/services/time.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/content/sheet.dart';
import 'package:island/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'bot_keys.g.dart';
@riverpod
Future<List<SnAccountApiKey>> botKeys(
Ref ref,
String publisherName,
String projectId,
String botId,
) async {
final client = ref.watch(apiClientProvider);
final resp = await client.get(
'/develop/developers/$publisherName/projects/$projectId/bots/$botId/keys',
);
return (resp.data as List).map((e) => SnAccountApiKey.fromJson(e)).toList();
}
class BotKeysScreen extends HookConsumerWidget {
final String publisherName;
final String projectId;
final String botId;
const BotKeysScreen({
super.key,
required this.publisherName,
required this.projectId,
required this.botId,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final keys = ref.watch(botKeysProvider(publisherName, projectId, botId));
final keyNameController = useTextEditingController();
void showNewKeySheet(SnAccountApiKey newApiKey) {
final token = newApiKey.key;
if (token == null) return;
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder:
(context) => SheetScaffold(
titleText: 'newKeyGenerated'.tr(),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
Text('copyKeyHint'.tr()),
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(8),
),
child: SelectableText(token),
),
const SizedBox(height: 20),
FilledButton.icon(
onPressed: () {
Clipboard.setData(ClipboardData(text: token));
},
icon: const Icon(Symbols.copy_all),
label: Text('copy'.tr()),
),
],
),
),
),
).whenComplete(() {
ref.invalidate(botKeysProvider(publisherName, projectId, botId));
});
}
void createKey() {
keyNameController.clear();
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder:
(context) => SheetScaffold(
titleText: 'newBotKey'.tr(),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: keyNameController,
decoration: InputDecoration(labelText: 'keyName'.tr()),
autofocus: true,
),
const SizedBox(height: 20),
FilledButton.icon(
onPressed: () async {
if (keyNameController.text.isEmpty) return;
final keyName = keyNameController.text;
Navigator.pop(context); // Close the sheet
try {
final client = ref.read(apiClientProvider);
final resp = await client.post(
'/develop/developers/$publisherName/projects/$projectId/bots/$botId/keys',
data: {'label': keyName},
);
final newApiKey = SnAccountApiKey.fromJson(resp.data);
showNewKeySheet(newApiKey);
} catch (e) {
showErrorAlert(e.toString());
}
},
icon: const Icon(Symbols.add),
label: Text('create'.tr()),
),
],
),
),
),
);
}
void rotateKey(String keyId) {
showConfirmAlert('rotateBotKeyHint'.tr(), 'rotateBotKey'.tr()).then((
confirm,
) async {
if (confirm) {
try {
if (context.mounted) showLoadingModal(context);
final client = ref.read(apiClientProvider);
final resp = await client.post(
'/develop/developers/$publisherName/projects/$projectId/bots/$botId/keys/$keyId/rotate',
);
final rotatedApiKey = SnAccountApiKey.fromJson(resp.data);
showNewKeySheet(rotatedApiKey);
} catch (err) {
showErrorAlert(err.toString());
} finally {
if (context.mounted) hideLoadingModal(context);
}
}
});
}
void revokeKey(String keyId) {
showConfirmAlert('revokeBotKeyHint'.tr(), 'revokeBotKey'.tr()).then((
confirm,
) {
if (confirm) {
final client = ref.read(apiClientProvider);
client
.delete(
'/develop/developers/$publisherName/projects/$projectId/bots/$botId/keys/$keyId',
)
.then((_) {
ref.invalidate(
botKeysProvider(publisherName, projectId, botId),
);
})
.catchError((err) {
showErrorAlert(err.toString());
});
}
});
}
return keys.when(
data: (data) {
return Column(
children: [
ListTile(
leading: const Icon(Symbols.add),
title: Text('newBotKey'.tr()),
trailing: const Icon(Symbols.chevron_right),
onTap: createKey,
),
const Divider(height: 1),
Expanded(
child:
data.isEmpty
? Center(child: Text('noBotKeys'.tr()))
: RefreshIndicator(
onRefresh:
() => ref.refresh(
botKeysProvider(
publisherName,
projectId,
botId,
).future,
),
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: data.length,
itemBuilder: (context, index) {
final apiKey = data[index];
return ListTile(
title: Text(apiKey.label),
subtitle: Text(apiKey.createdAt.formatSystem()),
contentPadding: EdgeInsets.only(
left: 16,
right: 12,
),
trailing: PopupMenuButton(
itemBuilder:
(context) => [
PopupMenuItem(
value: 'rotate',
child: Row(
children: [
const Icon(Symbols.refresh),
const Gap(12),
Text('rotateKey'.tr()),
],
),
),
PopupMenuItem(
value: 'revoke',
child: Row(
children: [
const Icon(
Symbols.delete,
color: Colors.red,
),
const Gap(12),
Text(
'revoke'.tr(),
style: TextStyle(
color: Colors.red,
),
),
],
),
),
],
onSelected: (value) {
if (value == 'rotate') {
rotateKey(apiKey.id);
} else if (value == 'revoke') {
revokeKey(apiKey.id);
}
},
),
);
},
),
),
),
],
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error:
(err, stack) => ResponseErrorWidget(
error: err,
onRetry:
() => ref.invalidate(
botKeysProvider(publisherName, projectId, botId),
),
),
);
}
}

View File

@@ -0,0 +1,172 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'bot_keys.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$botKeysHash() => r'f7d1121833dc3da0cbd84b6171c2b2539edeb785';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [botKeys].
@ProviderFor(botKeys)
const botKeysProvider = BotKeysFamily();
/// See also [botKeys].
class BotKeysFamily extends Family<AsyncValue<List<SnAccountApiKey>>> {
/// See also [botKeys].
const BotKeysFamily();
/// See also [botKeys].
BotKeysProvider call(String publisherName, String projectId, String botId) {
return BotKeysProvider(publisherName, projectId, botId);
}
@override
BotKeysProvider getProviderOverride(covariant BotKeysProvider provider) {
return call(provider.publisherName, provider.projectId, provider.botId);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'botKeysProvider';
}
/// See also [botKeys].
class BotKeysProvider extends AutoDisposeFutureProvider<List<SnAccountApiKey>> {
/// See also [botKeys].
BotKeysProvider(String publisherName, String projectId, String botId)
: this._internal(
(ref) => botKeys(ref as BotKeysRef, publisherName, projectId, botId),
from: botKeysProvider,
name: r'botKeysProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$botKeysHash,
dependencies: BotKeysFamily._dependencies,
allTransitiveDependencies: BotKeysFamily._allTransitiveDependencies,
publisherName: publisherName,
projectId: projectId,
botId: botId,
);
BotKeysProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.publisherName,
required this.projectId,
required this.botId,
}) : super.internal();
final String publisherName;
final String projectId;
final String botId;
@override
Override overrideWith(
FutureOr<List<SnAccountApiKey>> Function(BotKeysRef provider) create,
) {
return ProviderOverride(
origin: this,
override: BotKeysProvider._internal(
(ref) => create(ref as BotKeysRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
publisherName: publisherName,
projectId: projectId,
botId: botId,
),
);
}
@override
AutoDisposeFutureProviderElement<List<SnAccountApiKey>> createElement() {
return _BotKeysProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is BotKeysProvider &&
other.publisherName == publisherName &&
other.projectId == projectId &&
other.botId == botId;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, publisherName.hashCode);
hash = _SystemHash.combine(hash, projectId.hashCode);
hash = _SystemHash.combine(hash, botId.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin BotKeysRef on AutoDisposeFutureProviderRef<List<SnAccountApiKey>> {
/// The parameter `publisherName` of this provider.
String get publisherName;
/// The parameter `projectId` of this provider.
String get projectId;
/// The parameter `botId` of this provider.
String get botId;
}
class _BotKeysProviderElement
extends AutoDisposeFutureProviderElement<List<SnAccountApiKey>>
with BotKeysRef {
_BotKeysProviderElement(super.provider);
@override
String get publisherName => (origin as BotKeysProvider).publisherName;
@override
String get projectId => (origin as BotKeysProvider).projectId;
@override
String get botId => (origin as BotKeysProvider).botId;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -0,0 +1,167 @@
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/bot.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/response.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'bots.g.dart';
@riverpod
Future<List<Bot>> bots(Ref ref, String publisherName, String projectId) async {
final client = ref.watch(apiClientProvider);
final resp = await client.get(
'/develop/developers/$publisherName/projects/$projectId/bots',
);
return (resp.data as List).map((e) => Bot.fromJson(e)).cast<Bot>().toList();
}
class BotsScreen extends HookConsumerWidget {
final String publisherName;
final String projectId;
const BotsScreen({
super.key,
required this.publisherName,
required this.projectId,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final botsList = ref.watch(botsProvider(publisherName, projectId));
return botsList.when(
data: (data) {
if (data.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('noBots').tr(),
const SizedBox(height: 16),
ElevatedButton.icon(
onPressed: () {
context.pushNamed(
'developerBotNew',
pathParameters: {
'name': publisherName,
'projectId': projectId,
},
);
},
icon: const Icon(Symbols.add),
label: Text('createBot').tr(),
),
],
),
);
}
return RefreshIndicator(
onRefresh:
() => ref.refresh(botsProvider(publisherName, projectId).future),
child: ListView.builder(
padding: const EdgeInsets.only(top: 4),
itemCount: data.length,
itemBuilder: (context, index) {
final bot = data[index];
return Card(
margin: const EdgeInsets.all(8.0),
child: ListTile(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
),
leading: CircleAvatar(
child:
bot.account.profile.picture != null
? ProfilePictureWidget(
file: bot.account.profile.picture!,
)
: const Icon(Symbols.smart_toy),
),
title: Text(bot.account.nick),
subtitle: Text(bot.account.name),
trailing: PopupMenuButton(
itemBuilder:
(context) => [
PopupMenuItem(
value: 'edit',
child: Row(
children: [
const Icon(Symbols.edit),
const SizedBox(width: 12),
Text('edit').tr(),
],
),
),
PopupMenuItem(
value: 'delete',
child: Row(
children: [
const Icon(Symbols.delete, color: Colors.red),
const SizedBox(width: 12),
Text(
'delete',
style: TextStyle(color: Colors.red),
).tr(),
],
),
),
],
onSelected: (value) {
if (value == 'edit') {
context.pushNamed(
'developerBotEdit',
pathParameters: {
'name': publisherName,
'projectId': projectId,
'id': bot.id,
},
);
} else if (value == 'delete') {
showConfirmAlert(
'deleteBotHint'.tr(),
'deleteBot'.tr(),
).then((confirm) {
if (confirm) {
final client = ref.read(apiClientProvider);
client.delete(
'/develop/developers/$publisherName/projects/$projectId/bots/${bot.id}',
);
ref.invalidate(
botsProvider(publisherName, projectId),
);
}
});
}
},
),
onTap: () {
context.pushNamed(
'developerBotDetail',
pathParameters: {
'name': publisherName,
'projectId': projectId,
'botId': bot.id,
},
);
},
),
);
},
),
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error:
(err, stack) => ResponseErrorWidget(
error: err,
onRetry:
() => ref.invalidate(botsProvider(publisherName, projectId)),
),
);
}
}

View File

@@ -0,0 +1,156 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'bots.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$botsHash() => r'15cefd5781350eb68208a342e85fcb0b9e0e3269';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [bots].
@ProviderFor(bots)
const botsProvider = BotsFamily();
/// See also [bots].
class BotsFamily extends Family<AsyncValue<List<Bot>>> {
/// See also [bots].
const BotsFamily();
/// See also [bots].
BotsProvider call(String publisherName, String projectId) {
return BotsProvider(publisherName, projectId);
}
@override
BotsProvider getProviderOverride(covariant BotsProvider provider) {
return call(provider.publisherName, provider.projectId);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'botsProvider';
}
/// See also [bots].
class BotsProvider extends AutoDisposeFutureProvider<List<Bot>> {
/// See also [bots].
BotsProvider(String publisherName, String projectId)
: this._internal(
(ref) => bots(ref as BotsRef, publisherName, projectId),
from: botsProvider,
name: r'botsProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$botsHash,
dependencies: BotsFamily._dependencies,
allTransitiveDependencies: BotsFamily._allTransitiveDependencies,
publisherName: publisherName,
projectId: projectId,
);
BotsProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.publisherName,
required this.projectId,
}) : super.internal();
final String publisherName;
final String projectId;
@override
Override overrideWith(FutureOr<List<Bot>> Function(BotsRef provider) create) {
return ProviderOverride(
origin: this,
override: BotsProvider._internal(
(ref) => create(ref as BotsRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
publisherName: publisherName,
projectId: projectId,
),
);
}
@override
AutoDisposeFutureProviderElement<List<Bot>> createElement() {
return _BotsProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is BotsProvider &&
other.publisherName == publisherName &&
other.projectId == projectId;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, publisherName.hashCode);
hash = _SystemHash.combine(hash, projectId.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin BotsRef on AutoDisposeFutureProviderRef<List<Bot>> {
/// The parameter `publisherName` of this provider.
String get publisherName;
/// The parameter `projectId` of this provider.
String get projectId;
}
class _BotsProviderElement extends AutoDisposeFutureProviderElement<List<Bot>>
with BotsRef {
_BotsProviderElement(super.provider);
@override
String get publisherName => (origin as BotsProvider).publisherName;
@override
String get projectId => (origin as BotsProvider).projectId;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -22,21 +22,37 @@ import 'package:island/widgets/content/sheet.dart';
part 'edit_app.g.dart'; part 'edit_app.g.dart';
@riverpod @riverpod
Future<CustomApp?> customApp(Ref ref, String publisherName, String id) async { Future<CustomApp?> customApp(
Ref ref,
String publisherName,
String projectId,
String id,
) async {
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
final resp = await client.get('/develop/developers/$publisherName/apps/$id'); final resp = await client.get(
'/develop/developers/$publisherName/projects/$projectId/apps/$id',
);
return CustomApp.fromJson(resp.data); return CustomApp.fromJson(resp.data);
} }
class EditAppScreen extends HookConsumerWidget { class EditAppScreen extends HookConsumerWidget {
final String publisherName; final String publisherName;
final String projectId;
final String? id; final String? id;
const EditAppScreen({super.key, required this.publisherName, this.id}); const EditAppScreen({
super.key,
required this.publisherName,
required this.projectId,
this.id,
});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final isNew = id == null; final isNew = id == null;
final app = isNew ? null : ref.watch(customAppProvider(publisherName, id!)); final app =
isNew
? null
: ref.watch(customAppProvider(publisherName, projectId, id!));
final formKey = useMemoized(() => GlobalKey<FormState>()); final formKey = useMemoized(() => GlobalKey<FormState>());
@@ -281,18 +297,26 @@ class EditAppScreen extends HookConsumerWidget {
} }
: null, : null,
}; };
if (isNew) { try {
await client.post( showLoadingModal(context);
'/develop/developers/$publisherName/apps', if (isNew) {
data: data, await client.post(
); '/develop/developers/$publisherName/projects/$projectId/apps',
} else { data: data,
await client.patch( );
'/develop/developers/$publisherName/apps/$id', } else {
data: data, await client.patch(
); '/develop/developers/$publisherName/projects/$projectId/apps/$id',
data: data,
);
}
} catch (err) {
showErrorAlert(err);
return;
} finally {
if (context.mounted) hideLoadingModal(context);
} }
ref.invalidate(customAppsProvider(publisherName)); ref.invalidate(customAppsProvider(publisherName, projectId));
if (context.mounted) { if (context.mounted) {
Navigator.pop(context); Navigator.pop(context);
} }
@@ -309,7 +333,9 @@ class EditAppScreen extends HookConsumerWidget {
? ResponseErrorWidget( ? ResponseErrorWidget(
error: app!.error, error: app!.error,
onRetry: onRetry:
() => ref.invalidate(customAppProvider(publisherName, id!)), () => ref.invalidate(
customAppProvider(publisherName, projectId, id!),
),
) )
: SingleChildScrollView( : SingleChildScrollView(
child: Column( child: Column(

View File

@@ -6,7 +6,7 @@ part of 'edit_app.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$customAppHash() => r'42ad937b8439c793e3c5c35568bb5fa4da017df3'; String _$customAppHash() => r'8e1b38f3dc9b04fad362ee1141fcbfc53f008c09';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {
@@ -39,13 +39,13 @@ class CustomAppFamily extends Family<AsyncValue<CustomApp?>> {
const CustomAppFamily(); const CustomAppFamily();
/// See also [customApp]. /// See also [customApp].
CustomAppProvider call(String publisherName, String id) { CustomAppProvider call(String publisherName, String projectId, String id) {
return CustomAppProvider(publisherName, id); return CustomAppProvider(publisherName, projectId, id);
} }
@override @override
CustomAppProvider getProviderOverride(covariant CustomAppProvider provider) { CustomAppProvider getProviderOverride(covariant CustomAppProvider provider) {
return call(provider.publisherName, provider.id); return call(provider.publisherName, provider.projectId, provider.id);
} }
static const Iterable<ProviderOrFamily>? _dependencies = null; static const Iterable<ProviderOrFamily>? _dependencies = null;
@@ -66,9 +66,9 @@ class CustomAppFamily extends Family<AsyncValue<CustomApp?>> {
/// See also [customApp]. /// See also [customApp].
class CustomAppProvider extends AutoDisposeFutureProvider<CustomApp?> { class CustomAppProvider extends AutoDisposeFutureProvider<CustomApp?> {
/// See also [customApp]. /// See also [customApp].
CustomAppProvider(String publisherName, String id) CustomAppProvider(String publisherName, String projectId, String id)
: this._internal( : this._internal(
(ref) => customApp(ref as CustomAppRef, publisherName, id), (ref) => customApp(ref as CustomAppRef, publisherName, projectId, id),
from: customAppProvider, from: customAppProvider,
name: r'customAppProvider', name: r'customAppProvider',
debugGetCreateSourceHash: debugGetCreateSourceHash:
@@ -78,6 +78,7 @@ class CustomAppProvider extends AutoDisposeFutureProvider<CustomApp?> {
dependencies: CustomAppFamily._dependencies, dependencies: CustomAppFamily._dependencies,
allTransitiveDependencies: CustomAppFamily._allTransitiveDependencies, allTransitiveDependencies: CustomAppFamily._allTransitiveDependencies,
publisherName: publisherName, publisherName: publisherName,
projectId: projectId,
id: id, id: id,
); );
@@ -89,10 +90,12 @@ class CustomAppProvider extends AutoDisposeFutureProvider<CustomApp?> {
required super.debugGetCreateSourceHash, required super.debugGetCreateSourceHash,
required super.from, required super.from,
required this.publisherName, required this.publisherName,
required this.projectId,
required this.id, required this.id,
}) : super.internal(); }) : super.internal();
final String publisherName; final String publisherName;
final String projectId;
final String id; final String id;
@override @override
@@ -109,6 +112,7 @@ class CustomAppProvider extends AutoDisposeFutureProvider<CustomApp?> {
allTransitiveDependencies: null, allTransitiveDependencies: null,
debugGetCreateSourceHash: null, debugGetCreateSourceHash: null,
publisherName: publisherName, publisherName: publisherName,
projectId: projectId,
id: id, id: id,
), ),
); );
@@ -123,6 +127,7 @@ class CustomAppProvider extends AutoDisposeFutureProvider<CustomApp?> {
bool operator ==(Object other) { bool operator ==(Object other) {
return other is CustomAppProvider && return other is CustomAppProvider &&
other.publisherName == publisherName && other.publisherName == publisherName &&
other.projectId == projectId &&
other.id == id; other.id == id;
} }
@@ -130,6 +135,7 @@ class CustomAppProvider extends AutoDisposeFutureProvider<CustomApp?> {
int get hashCode { int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode); var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, publisherName.hashCode); hash = _SystemHash.combine(hash, publisherName.hashCode);
hash = _SystemHash.combine(hash, projectId.hashCode);
hash = _SystemHash.combine(hash, id.hashCode); hash = _SystemHash.combine(hash, id.hashCode);
return _SystemHash.finish(hash); return _SystemHash.finish(hash);
@@ -142,6 +148,9 @@ mixin CustomAppRef on AutoDisposeFutureProviderRef<CustomApp?> {
/// The parameter `publisherName` of this provider. /// The parameter `publisherName` of this provider.
String get publisherName; String get publisherName;
/// The parameter `projectId` of this provider.
String get projectId;
/// The parameter `id` of this provider. /// The parameter `id` of this provider.
String get id; String get id;
} }
@@ -154,6 +163,8 @@ class _CustomAppProviderElement
@override @override
String get publisherName => (origin as CustomAppProvider).publisherName; String get publisherName => (origin as CustomAppProvider).publisherName;
@override @override
String get projectId => (origin as CustomAppProvider).projectId;
@override
String get id => (origin as CustomAppProvider).id; String get id => (origin as CustomAppProvider).id;
} }

View File

@@ -0,0 +1,425 @@
import 'package:croppy/croppy.dart' hide cropImage;
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import 'package:island/models/bot.dart';
import 'package:island/models/file.dart';
import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart';
import 'package:island/services/file.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart';
part 'edit_bot.g.dart';
@riverpod
Future<Bot?> bot(
Ref ref,
String publisherName,
String projectId,
String id,
) async {
final client = ref.watch(apiClientProvider);
final resp = await client.get(
'/develop/developers/$publisherName/projects/$projectId/bots/$id',
);
return Bot.fromJson(resp.data);
}
class EditBotScreen extends HookConsumerWidget {
final String publisherName;
final String projectId;
final String? id;
const EditBotScreen({
super.key,
required this.publisherName,
required this.projectId,
this.id,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isNew = id == null;
final botData =
isNew ? null : ref.watch(botProvider(publisherName, projectId, id!));
final formKey = useMemoized(() => GlobalKey<FormState>());
final submitting = useState(false);
final nameController = useTextEditingController();
final nickController = useTextEditingController();
final slugController = useTextEditingController();
final picture = useState<SnCloudFile?>(null);
final firstNameController = useTextEditingController();
final middleNameController = useTextEditingController();
final lastNameController = useTextEditingController();
final genderController = useTextEditingController();
final pronounsController = useTextEditingController();
final locationController = useTextEditingController();
final timeZoneController = useTextEditingController();
final bioController = useTextEditingController();
final birthday = useState<DateTime?>(null);
final background = useState<SnCloudFile?>(null);
useEffect(() {
if (botData?.value != null) {
nameController.text = botData!.value!.account.name;
nickController.text = botData.value!.account.nick;
slugController.text = botData.value!.slug;
picture.value = botData.value!.account.profile.picture;
background.value = botData.value!.account.profile.background;
// Populate from botData.value.account.profile
firstNameController.text = botData.value!.account.profile.firstName;
middleNameController.text = botData.value!.account.profile.middleName;
lastNameController.text = botData.value!.account.profile.lastName;
genderController.text = botData.value!.account.profile.gender;
pronounsController.text = botData.value!.account.profile.pronouns;
locationController.text = botData.value!.account.profile.location;
timeZoneController.text = botData.value!.account.profile.timeZone;
bioController.text = botData.value!.account.profile.bio;
birthday.value = botData.value!.account.profile.birthday?.toLocal();
}
return null;
}, [botData]);
void setPicture(String position) async {
showLoadingModal(context);
var result = await ref
.read(imagePickerProvider)
.pickImage(source: ImageSource.gallery);
if (result == null) {
if (context.mounted) hideLoadingModal(context);
return;
}
if (!context.mounted) return;
hideLoadingModal(context);
result = await cropImage(
context,
image: result,
allowedAspectRatios: [
if (position == 'background')
const CropAspectRatio(height: 7, width: 16)
else
const CropAspectRatio(height: 1, width: 1),
],
);
if (result == null) {
if (context.mounted) hideLoadingModal(context);
return;
}
if (!context.mounted) return;
showLoadingModal(context);
submitting.value = true;
try {
final baseUrl = ref.watch(serverUrlProvider);
final token = await getToken(ref.watch(tokenProvider));
if (token == null) throw ArgumentError('Token is null');
final cloudFile =
await putMediaToCloud(
fileData: UniversalFile(
data: result,
type: UniversalFileType.image,
),
atk: token,
baseUrl: baseUrl,
filename: result.name,
mimetype: result.mimeType ?? 'image/jpeg',
).future;
if (cloudFile == null) {
throw ArgumentError('Failed to upload the file...');
}
switch (position) {
case 'picture':
picture.value = cloudFile;
case 'background':
background.value = cloudFile;
}
} catch (err) {
showErrorAlert(err);
} finally {
if (context.mounted) hideLoadingModal(context);
submitting.value = false;
}
}
void performAction() async {
final client = ref.read(apiClientProvider);
final data = {
'name': nameController.text,
'nick': nickController.text,
'slug': slugController.text,
'picture_id': picture.value?.id,
'background_id': background.value?.id,
'first_name': firstNameController.text,
'middle_name': middleNameController.text,
'last_name': lastNameController.text,
'gender': genderController.text,
'pronouns': pronounsController.text,
'location': locationController.text,
'time_zone': timeZoneController.text,
'bio': bioController.text,
'birthday': birthday.value?.toUtc().toIso8601String(),
};
try {
showLoadingModal(context);
if (isNew) {
await client.post(
'/develop/developers/$publisherName/projects/$projectId/bots',
data: data,
);
} else {
await client.patch(
'/develop/developers/$publisherName/projects/$projectId/bots/$id',
data: data,
);
}
if (context.mounted) {
context.pop();
}
} catch (err) {
showErrorAlert(err);
} finally {
if (context.mounted) hideLoadingModal(context);
}
}
return AppScaffold(
appBar: AppBar(title: Text(isNew ? 'createBot'.tr() : 'editBot'.tr())),
body:
botData == null && !isNew
? const Center(child: CircularProgressIndicator())
: botData?.hasError == true && !isNew
? ResponseErrorWidget(
error: botData!.error,
onRetry:
() => ref.invalidate(
botProvider(publisherName, projectId, id!),
),
)
: SingleChildScrollView(
child: Column(
children: [
AspectRatio(
aspectRatio: 16 / 7,
child: Stack(
clipBehavior: Clip.none,
fit: StackFit.expand,
children: [
GestureDetector(
child: Container(
color:
Theme.of(
context,
).colorScheme.surfaceContainerHigh,
child:
background.value != null
? CloudFileWidget(
item: background.value!,
fit: BoxFit.cover,
)
: const SizedBox.shrink(),
),
onTap: () {
setPicture('background');
},
),
Positioned(
left: 20,
bottom: -32,
child: GestureDetector(
child: ProfilePictureWidget(
fileId: picture.value?.id,
radius: 40,
fallbackIcon: Symbols.smart_toy,
),
onTap: () {
setPicture('picture');
},
),
),
],
),
).padding(bottom: 32),
Form(
key: formKey,
child: Column(
children: [
TextFormField(
controller: nameController,
decoration: InputDecoration(labelText: 'name'.tr()),
),
const SizedBox(height: 16),
TextFormField(
controller: nickController,
decoration: InputDecoration(
labelText: 'nickname'.tr(),
alignLabelWithHint: true,
),
),
const SizedBox(height: 16),
TextFormField(
controller: slugController,
decoration: InputDecoration(
labelText: 'slug'.tr(),
helperText: 'slugHint'.tr(),
),
),
const SizedBox(height: 16),
TextFormField(
controller: bioController,
decoration: InputDecoration(
labelText: 'bio'.tr(),
alignLabelWithHint: true,
),
maxLines: 3,
),
const SizedBox(height: 16),
Row(
spacing: 16,
children: [
Expanded(
child: TextFormField(
controller: firstNameController,
decoration: InputDecoration(
labelText: 'firstName'.tr(),
),
),
),
Expanded(
child: TextFormField(
controller: middleNameController,
decoration: InputDecoration(
labelText: 'middleName'.tr(),
),
),
),
Expanded(
child: TextFormField(
controller: lastNameController,
decoration: InputDecoration(
labelText: 'lastName'.tr(),
),
),
),
],
),
const SizedBox(height: 16),
Row(
spacing: 16,
children: [
Expanded(
child: TextFormField(
controller: genderController,
decoration: InputDecoration(
labelText: 'gender'.tr(),
),
),
),
Expanded(
child: TextFormField(
controller: pronounsController,
decoration: InputDecoration(
labelText: 'pronouns'.tr(),
),
),
),
],
),
const SizedBox(height: 16),
Row(
spacing: 16,
children: [
Expanded(
child: TextFormField(
controller: locationController,
decoration: InputDecoration(
labelText: 'location'.tr(),
),
),
),
Expanded(
child: TextFormField(
controller: timeZoneController,
decoration: InputDecoration(
labelText: 'timeZone'.tr(),
),
),
),
],
),
const SizedBox(height: 16),
GestureDetector(
onTap: () async {
final date = await showDatePicker(
context: context,
initialDate: birthday.value ?? DateTime.now(),
firstDate: DateTime(1900),
lastDate: DateTime.now(),
);
if (date != null) {
birthday.value = date;
}
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).dividerColor,
width: 1,
),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'birthday'.tr(),
style: TextStyle(
color: Theme.of(context).hintColor,
),
),
Text(
birthday.value != null
? DateFormat.yMMMd().format(
birthday.value!,
)
: 'Select a date'.tr(),
),
],
),
),
),
const SizedBox(height: 16),
Align(
alignment: Alignment.centerRight,
child: TextButton.icon(
onPressed:
submitting.value ? null : performAction,
label: Text('saveChanges').tr(),
icon: const Icon(Symbols.save),
),
),
],
).padding(all: 24),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,167 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'edit_bot.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$botHash() => r'7bec47bb2a4061a5babc6d6d19c3d4c320c91188';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [bot].
@ProviderFor(bot)
const botProvider = BotFamily();
/// See also [bot].
class BotFamily extends Family<AsyncValue<Bot?>> {
/// See also [bot].
const BotFamily();
/// See also [bot].
BotProvider call(String publisherName, String projectId, String id) {
return BotProvider(publisherName, projectId, id);
}
@override
BotProvider getProviderOverride(covariant BotProvider provider) {
return call(provider.publisherName, provider.projectId, provider.id);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'botProvider';
}
/// See also [bot].
class BotProvider extends AutoDisposeFutureProvider<Bot?> {
/// See also [bot].
BotProvider(String publisherName, String projectId, String id)
: this._internal(
(ref) => bot(ref as BotRef, publisherName, projectId, id),
from: botProvider,
name: r'botProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$botHash,
dependencies: BotFamily._dependencies,
allTransitiveDependencies: BotFamily._allTransitiveDependencies,
publisherName: publisherName,
projectId: projectId,
id: id,
);
BotProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.publisherName,
required this.projectId,
required this.id,
}) : super.internal();
final String publisherName;
final String projectId;
final String id;
@override
Override overrideWith(FutureOr<Bot?> Function(BotRef provider) create) {
return ProviderOverride(
origin: this,
override: BotProvider._internal(
(ref) => create(ref as BotRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
publisherName: publisherName,
projectId: projectId,
id: id,
),
);
}
@override
AutoDisposeFutureProviderElement<Bot?> createElement() {
return _BotProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is BotProvider &&
other.publisherName == publisherName &&
other.projectId == projectId &&
other.id == id;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, publisherName.hashCode);
hash = _SystemHash.combine(hash, projectId.hashCode);
hash = _SystemHash.combine(hash, id.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin BotRef on AutoDisposeFutureProviderRef<Bot?> {
/// The parameter `publisherName` of this provider.
String get publisherName;
/// The parameter `projectId` of this provider.
String get projectId;
/// The parameter `id` of this provider.
String get id;
}
class _BotProviderElement extends AutoDisposeFutureProviderElement<Bot?>
with BotRef {
_BotProviderElement(super.provider);
@override
String get publisherName => (origin as BotProvider).publisherName;
@override
String get projectId => (origin as BotProvider).projectId;
@override
String get id => (origin as BotProvider).id;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -0,0 +1,130 @@
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/dev_project.dart';
import 'package:island/pods/network.dart';
import 'package:island/screens/developers/projects.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart';
part 'edit_project.g.dart';
@riverpod
Future<DevProject?> devProject(Ref ref, String pubName, String id) async {
final client = ref.watch(apiClientProvider);
final resp = await client.get('/develop/developers/$pubName/projects/$id');
return DevProject.fromJson(resp.data);
}
class EditProjectScreen extends HookConsumerWidget {
final String publisherName;
final String? id;
const EditProjectScreen({super.key, required this.publisherName, this.id});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isNew = id == null;
final projectData =
isNew ? null : ref.watch(devProjectProvider(publisherName, id!));
final formKey = useMemoized(() => GlobalKey<FormState>());
final submitting = useState(false);
final nameController = useTextEditingController();
final slugController = useTextEditingController();
final descriptionController = useTextEditingController();
useEffect(() {
if (projectData?.value != null) {
nameController.text = projectData!.value!.name;
slugController.text = projectData.value!.slug;
descriptionController.text = projectData.value!.description ?? '';
}
return null;
}, [projectData]);
void performAction() async {
final client = ref.read(apiClientProvider);
final data = {
'name': nameController.text,
'slug': slugController.text,
'description': descriptionController.text,
};
if (isNew) {
await client.post(
'/develop/developers/$publisherName/projects',
data: data,
);
} else {
await client.put(
'/develop/developers/$publisherName/projects/$id',
data: data,
);
}
ref.invalidate(devProjectsProvider(publisherName));
if (context.mounted) {
context.pop();
}
}
return AppScaffold(
appBar: AppBar(
title: Text(isNew ? 'createProject'.tr() : 'editProject'.tr()),
),
body:
projectData == null && !isNew
? const Center(child: CircularProgressIndicator())
: projectData?.hasError == true && !isNew
? ResponseErrorWidget(
error: projectData!.error,
onRetry:
() =>
ref.invalidate(devProjectProvider(publisherName, id!)),
)
: SingleChildScrollView(
child: Form(
key: formKey,
child: Column(
children: [
TextFormField(
controller: nameController,
decoration: InputDecoration(labelText: 'name'.tr()),
),
const SizedBox(height: 16),
TextFormField(
controller: slugController,
decoration: InputDecoration(
labelText: 'slug'.tr(),
helperText: 'slugHint'.tr(),
),
),
const SizedBox(height: 16),
TextFormField(
controller: descriptionController,
decoration: InputDecoration(
labelText: 'description'.tr(),
alignLabelWithHint: true,
),
maxLines: 3,
),
const SizedBox(height: 16),
Align(
alignment: Alignment.centerRight,
child: TextButton.icon(
onPressed: submitting.value ? null : performAction,
label: Text('saveChanges'.tr()),
icon: const Icon(Symbols.save),
),
),
],
).padding(all: 24),
),
),
);
}
}

View File

@@ -0,0 +1,163 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'edit_project.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$devProjectHash() => r'd92be3f5cdc510c2a377615ed5c70622a6842bf2';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [devProject].
@ProviderFor(devProject)
const devProjectProvider = DevProjectFamily();
/// See also [devProject].
class DevProjectFamily extends Family<AsyncValue<DevProject?>> {
/// See also [devProject].
const DevProjectFamily();
/// See also [devProject].
DevProjectProvider call(String pubName, String id) {
return DevProjectProvider(pubName, id);
}
@override
DevProjectProvider getProviderOverride(
covariant DevProjectProvider provider,
) {
return call(provider.pubName, provider.id);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'devProjectProvider';
}
/// See also [devProject].
class DevProjectProvider extends AutoDisposeFutureProvider<DevProject?> {
/// See also [devProject].
DevProjectProvider(String pubName, String id)
: this._internal(
(ref) => devProject(ref as DevProjectRef, pubName, id),
from: devProjectProvider,
name: r'devProjectProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$devProjectHash,
dependencies: DevProjectFamily._dependencies,
allTransitiveDependencies: DevProjectFamily._allTransitiveDependencies,
pubName: pubName,
id: id,
);
DevProjectProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.pubName,
required this.id,
}) : super.internal();
final String pubName;
final String id;
@override
Override overrideWith(
FutureOr<DevProject?> Function(DevProjectRef provider) create,
) {
return ProviderOverride(
origin: this,
override: DevProjectProvider._internal(
(ref) => create(ref as DevProjectRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
pubName: pubName,
id: id,
),
);
}
@override
AutoDisposeFutureProviderElement<DevProject?> createElement() {
return _DevProjectProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is DevProjectProvider &&
other.pubName == pubName &&
other.id == id;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, pubName.hashCode);
hash = _SystemHash.combine(hash, id.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin DevProjectRef on AutoDisposeFutureProviderRef<DevProject?> {
/// The parameter `pubName` of this provider.
String get pubName;
/// The parameter `id` of this provider.
String get id;
}
class _DevProjectProviderElement
extends AutoDisposeFutureProviderElement<DevProject?>
with DevProjectRef {
_DevProjectProviderElement(super.provider);
@override
String get pubName => (origin as DevProjectProvider).pubName;
@override
String get id => (origin as DevProjectProvider).id;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -235,15 +235,15 @@ class DeveloperHubScreen extends HookConsumerWidget {
).padding(vertical: 12, horizontal: 12), ).padding(vertical: 12, horizontal: 12),
ListTile( ListTile(
minTileHeight: 48, minTileHeight: 48,
title: Text('customApps').tr(), title: Text('projects').tr(),
trailing: Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
leading: const Icon(Symbols.apps), leading: const Icon(Symbols.folder_managed),
contentPadding: EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(
horizontal: 24, horizontal: 24,
), ),
onTap: () { onTap: () {
context.pushNamed( context.pushNamed(
'developerApps', 'developerProjects',
pathParameters: { pathParameters: {
'name': 'name':
currentDeveloper.value!.publisher!.name, currentDeveloper.value!.publisher!.name,

View File

@@ -3,10 +3,11 @@ import 'package:island/screens/developers/edit_app.dart';
class NewCustomAppScreen extends StatelessWidget { class NewCustomAppScreen extends StatelessWidget {
final String publisherName; final String publisherName;
const NewCustomAppScreen({super.key, required this.publisherName}); final String projectId;
const NewCustomAppScreen({super.key, required this.publisherName, required this.projectId});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return EditAppScreen(publisherName: publisherName); return EditAppScreen(publisherName: publisherName, projectId: projectId);
} }
} }

View File

@@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
import 'package:island/screens/developers/edit_bot.dart';
class NewBotScreen extends StatelessWidget {
final String publisherName;
final String projectId;
const NewBotScreen({super.key, required this.publisherName, required this.projectId});
@override
Widget build(BuildContext context) {
return EditBotScreen(publisherName: publisherName, projectId: projectId);
}
}

View File

@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
import 'package:island/screens/developers/edit_project.dart';
class NewProjectScreen extends StatelessWidget {
final String publisherName;
const NewProjectScreen({super.key, required this.publisherName});
@override
Widget build(BuildContext context) {
return EditProjectScreen(publisherName: publisherName);
}
}

View File

@@ -0,0 +1,73 @@
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:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/screens/developers/apps.dart';
import 'package:island/screens/developers/bots.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:material_symbols_icons/symbols.dart';
class ProjectDetailScreen extends HookConsumerWidget {
final String publisherName;
final String projectId;
const ProjectDetailScreen({
super.key,
required this.publisherName,
required this.projectId,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final tabController = useTabController(initialLength: 2);
return AppScaffold(
appBar: AppBar(
title: Text('projectDetails').tr(),
actions: [
IconButton(
icon: const Icon(Symbols.add),
onPressed: () {
// Get current tab index
final index = tabController.index;
switch (index) {
case 0:
context.pushNamed(
'developerAppNew',
pathParameters: {
'name': publisherName,
'projectId': projectId,
},
);
break;
case 1:
context.pushNamed(
'developerBotNew',
pathParameters: {
'name': publisherName,
'projectId': projectId,
},
);
break;
}
},
),
const Gap(8),
],
bottom: TabBar(
controller: tabController,
tabs: [Tab(text: 'customApps'.tr()), Tab(text: 'bots'.tr())],
),
),
body: TabBarView(
controller: tabController,
children: [
CustomAppsScreen(publisherName: publisherName, projectId: projectId),
BotsScreen(publisherName: publisherName, projectId: projectId),
],
),
);
}
}

View File

@@ -0,0 +1,150 @@
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/dev_project.dart';
import 'package:island/pods/network.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'projects.g.dart';
@riverpod
Future<List<DevProject>> devProjects(Ref ref, String pubName) async {
final client = ref.watch(apiClientProvider);
final resp = await client.get('/develop/developers/$pubName/projects');
return (resp.data as List)
.map((e) => DevProject.fromJson(e))
.cast<DevProject>()
.toList();
}
class DevProjectsScreen extends HookConsumerWidget {
final String publisherName;
const DevProjectsScreen({super.key, required this.publisherName});
@override
Widget build(BuildContext context, WidgetRef ref) {
final projects = ref.watch(devProjectsProvider(publisherName));
return AppScaffold(
appBar: AppBar(
title: Text('projects').tr(),
actions: [
IconButton(
icon: const Icon(Symbols.add),
onPressed: () {
context.pushNamed(
'developerProjectNew',
pathParameters: {'name': publisherName},
);
},
),
const Gap(8),
],
),
body: projects.when(
data: (data) {
if (data.isEmpty) {
return Center(child: Text('noProjects').tr());
}
return RefreshIndicator(
onRefresh:
() => ref.refresh(devProjectsProvider(publisherName).future),
child: ListView.builder(
padding: EdgeInsets.only(top: 4),
itemCount: data.length,
itemBuilder: (context, index) {
final project = data[index];
return Card(
margin: const EdgeInsets.all(8.0),
child: ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
contentPadding: EdgeInsets.only(left: 20, right: 12),
title: Text(project.name),
subtitle: Text(project.description ?? ''),
trailing: PopupMenuButton(
itemBuilder:
(context) => [
PopupMenuItem(
value: 'edit',
child: Row(
children: [
const Icon(Symbols.edit),
const SizedBox(width: 12),
Text('edit').tr(),
],
),
),
PopupMenuItem(
value: 'delete',
child: Row(
children: [
const Icon(Symbols.delete, color: Colors.red),
const SizedBox(width: 12),
Text(
'delete',
style: TextStyle(color: Colors.red),
).tr(),
],
),
),
],
onSelected: (value) {
if (value == 'edit') {
context.pushNamed(
'developerProjectEdit',
pathParameters: {
'name': publisherName,
'id': project.id,
},
);
} else if (value == 'delete') {
showConfirmAlert(
'deleteProjectHint'.tr(),
'deleteProject'.tr(),
).then((confirm) {
if (confirm) {
final client = ref.read(apiClientProvider);
client.delete(
'/develop/developers/$publisherName/projects/${project.id}',
);
ref.invalidate(
devProjectsProvider(publisherName),
);
}
});
}
},
),
onTap: () {
context.pushNamed(
'developerProjectDetail',
pathParameters: {
'name': publisherName,
'projectId': project.id,
},
);
},
),
);
},
),
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error:
(err, stack) => ResponseErrorWidget(
error: err,
onRetry: () => ref.invalidate(devProjectsProvider(publisherName)),
),
),
);
}
}

View File

@@ -0,0 +1,151 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'projects.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$devProjectsHash() => r'87fdcab47cd7d79ab019a5625617abeb1ffa1f39';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [devProjects].
@ProviderFor(devProjects)
const devProjectsProvider = DevProjectsFamily();
/// See also [devProjects].
class DevProjectsFamily extends Family<AsyncValue<List<DevProject>>> {
/// See also [devProjects].
const DevProjectsFamily();
/// See also [devProjects].
DevProjectsProvider call(String pubName) {
return DevProjectsProvider(pubName);
}
@override
DevProjectsProvider getProviderOverride(
covariant DevProjectsProvider provider,
) {
return call(provider.pubName);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'devProjectsProvider';
}
/// See also [devProjects].
class DevProjectsProvider extends AutoDisposeFutureProvider<List<DevProject>> {
/// See also [devProjects].
DevProjectsProvider(String pubName)
: this._internal(
(ref) => devProjects(ref as DevProjectsRef, pubName),
from: devProjectsProvider,
name: r'devProjectsProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$devProjectsHash,
dependencies: DevProjectsFamily._dependencies,
allTransitiveDependencies: DevProjectsFamily._allTransitiveDependencies,
pubName: pubName,
);
DevProjectsProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.pubName,
}) : super.internal();
final String pubName;
@override
Override overrideWith(
FutureOr<List<DevProject>> Function(DevProjectsRef provider) create,
) {
return ProviderOverride(
origin: this,
override: DevProjectsProvider._internal(
(ref) => create(ref as DevProjectsRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
pubName: pubName,
),
);
}
@override
AutoDisposeFutureProviderElement<List<DevProject>> createElement() {
return _DevProjectsProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is DevProjectsProvider && other.pubName == pubName;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, pubName.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin DevProjectsRef on AutoDisposeFutureProviderRef<List<DevProject>> {
/// The parameter `pubName` of this provider.
String get pubName;
}
class _DevProjectsProviderElement
extends AutoDisposeFutureProviderElement<List<DevProject>>
with DevProjectsRef {
_DevProjectsProviderElement(super.provider);
@override
String get pubName => (origin as DevProjectsProvider).pubName;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -101,7 +101,7 @@ class SliverArticlesList extends ConsumerWidget {
publisherId: publisherId, publisherId: publisherId,
).notifier, ).notifier,
contentBuilder: contentBuilder:
(data, widgetCount, endItemView) => SliverList.builder( (data, widgetCount, endItemView) => SliverList.separated(
itemCount: widgetCount, itemCount: widgetCount,
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (index == widgetCount - 1) { if (index == widgetCount - 1) {
@@ -111,38 +111,98 @@ class SliverArticlesList extends ConsumerWidget {
final article = data.items[index]; final article = data.items[index];
return WebArticleCard(article: article, showDetails: true); return WebArticleCard(article: article, showDetails: true);
}, },
separatorBuilder: (context, index) => const SizedBox(height: 12),
), ),
); );
} }
} }
@riverpod
Future<List<SnWebFeed>> subscribedFeeds(Ref ref) async {
final client = ref.watch(apiClientProvider);
final response = await client.get('/sphere/feeds/subscribed');
final data = response.data as List<dynamic>;
return data.map((json) => SnWebFeed.fromJson(json)).toList();
}
class ArticlesScreen extends ConsumerWidget { class ArticlesScreen extends ConsumerWidget {
final String? feedId; const ArticlesScreen({super.key});
final String? publisherId;
final String? title;
const ArticlesScreen({super.key, this.feedId, this.publisherId, this.title});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
return AppScaffold( final subscribedFeedsAsync = ref.watch(subscribedFeedsProvider);
appBar: AppBar(title: Text(title ?? 'Articles')),
body: Center( return subscribedFeedsAsync.when(
child: ConstrainedBox( data: (feeds) {
constraints: const BoxConstraints(maxWidth: 560), return DefaultTabController(
child: CustomScrollView( length: feeds.length + 1,
slivers: [ child: AppScaffold(
SliverPadding( isNoBackground: false,
padding: const EdgeInsets.only(top: 8, left: 8, right: 8), appBar: AppBar(
sliver: SliverArticlesList( title: const Text('Articles'),
feedId: feedId, bottom: TabBar(
publisherId: publisherId, isScrollable: true,
), tabs: [
const Tab(text: 'All'),
...feeds.map((feed) => Tab(text: feed.title)),
],
), ),
], ),
body: TabBarView(
children: [
Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 560),
child: CustomScrollView(
slivers: [
SliverPadding(
padding: const EdgeInsets.only(
top: 12,
left: 8,
right: 8,
),
sliver: SliverArticlesList(),
),
],
),
),
),
...feeds.map((feed) {
return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 560),
child: CustomScrollView(
slivers: [
SliverPadding(
padding: const EdgeInsets.only(
top: 8,
left: 8,
right: 8,
),
sliver: SliverArticlesList(feedId: feed.id),
),
],
),
),
);
}),
],
),
),
);
},
loading:
() => AppScaffold(
isNoBackground: false,
appBar: AppBar(title: const Text('Articles')),
body: const Center(child: CircularProgressIndicator()),
),
error:
(err, stack) => AppScaffold(
isNoBackground: false,
appBar: AppBar(title: const Text('Articles')),
body: Center(child: Text('Error: $err')),
), ),
),
),
); );
} }
} }

View File

@@ -6,6 +6,25 @@ part of 'articles.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$subscribedFeedsHash() => r'5c0c8c30c5f543f6ea1d39786a6778f77ba5b3df';
/// See also [subscribedFeeds].
@ProviderFor(subscribedFeeds)
final subscribedFeedsProvider =
AutoDisposeFutureProvider<List<SnWebFeed>>.internal(
subscribedFeeds,
name: r'subscribedFeedsProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$subscribedFeedsHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef SubscribedFeedsRef = AutoDisposeFutureProviderRef<List<SnWebFeed>>;
String _$articlesListNotifierHash() => String _$articlesListNotifierHash() =>
r'579741af4d90c7c81f2e2697e57c4895b7a9dabc'; r'579741af4d90c7c81f2e2697e57c4895b7a9dabc';

View File

@@ -0,0 +1,240 @@
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/webfeed.dart';
import 'package:island/pods/network.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/web_article_card.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
import 'package:styled_widget/styled_widget.dart';
part 'feed_detail.g.dart';
@riverpod
Future<SnWebFeed> marketplaceWebFeed(Ref ref, String feedId) async {
final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get('/sphere/feeds/$feedId');
return SnWebFeed.fromJson(resp.data);
}
/// Provider for web feed articles content
@riverpod
class MarketplaceWebFeedContentNotifier
extends _$MarketplaceWebFeedContentNotifier
with CursorPagingNotifierMixin<SnWebArticle> {
static const int _pageSize = 20;
@override
Future<CursorPagingData<SnWebArticle>> build(String feedId) async {
_feedId = feedId;
return fetch(cursor: null);
}
late final String _feedId;
ValueNotifier<int> totalCount = ValueNotifier(0);
@override
Future<CursorPagingData<SnWebArticle>> fetch({
required String? cursor,
}) async {
final client = ref.read(apiClientProvider);
final offset = cursor == null ? 0 : int.parse(cursor);
final queryParams = {'offset': offset, 'take': _pageSize};
final response = await client.get(
'/sphere/feeds/$_feedId/articles',
queryParameters: queryParams,
);
final total = int.parse(response.headers.value('X-Total') ?? '0');
totalCount.value = total;
final List<dynamic> data = response.data;
final articles = data.map((json) => SnWebArticle.fromJson(json)).toList();
final hasMore = offset + articles.length < total;
final nextCursor = hasMore ? (offset + articles.length).toString() : null;
return CursorPagingData(
items: articles,
hasMore: hasMore,
nextCursor: nextCursor,
);
}
void dispose() {
totalCount.dispose();
}
}
/// Provider for web feed subscription status
@riverpod
Future<bool> marketplaceWebFeedSubscription(
Ref ref, {
required String feedId,
}) async {
final api = ref.watch(apiClientProvider);
try {
await api.get('/sphere/feeds/$feedId/subscription');
// If not 404, consider subscribed
return true;
} on Object catch (e) {
// Dio error handling agnostic: treat 404 as not-subscribed, rethrow others
final msg = e.toString();
if (msg.contains('404')) return false;
rethrow;
}
}
class MarketplaceWebFeedDetailScreen extends HookConsumerWidget {
final String id;
const MarketplaceWebFeedDetailScreen({super.key, required this.id});
@override
Widget build(BuildContext context, WidgetRef ref) {
final feed = ref.watch(marketplaceWebFeedProvider(id));
final subscribed = ref.watch(
marketplaceWebFeedSubscriptionProvider(feedId: id),
);
// Subscribe to web feed
Future<void> subscribeToFeed() async {
final apiClient = ref.watch(apiClientProvider);
await apiClient.post('/sphere/feeds/$id/subscribe');
HapticFeedback.selectionClick();
ref.invalidate(marketplaceWebFeedSubscriptionProvider(feedId: id));
if (!context.mounted) return;
showSnackBar('webFeedSubscribed'.tr());
}
// Unsubscribe from web feed
Future<void> unsubscribeFromFeed() async {
final apiClient = ref.watch(apiClientProvider);
await apiClient.delete('/sphere/feeds/$id/subscribe');
HapticFeedback.selectionClick();
ref.invalidate(marketplaceWebFeedSubscriptionProvider(feedId: id));
if (!context.mounted) return;
showSnackBar('webFeedUnsubscribed'.tr());
}
final feedNotifier = ref.watch(
marketplaceWebFeedContentNotifierProvider(id).notifier,
);
useEffect(() {
return feedNotifier.dispose;
}, []);
return AppScaffold(
appBar: AppBar(title: Text(feed.value?.title ?? 'loading'.tr())),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Feed meta
feed
.when(
data:
(data) => Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(data.description ?? 'descriptionNone'.tr()),
Row(
spacing: 4,
children: [
const Icon(Symbols.rss_feed, size: 16),
ListenableBuilder(
listenable: feedNotifier.totalCount,
builder:
(context, _) => Text(
'webFeedArticleCount'.plural(
feedNotifier.totalCount.value,
),
),
),
],
).opacity(0.85),
Row(
spacing: 4,
children: [
const Icon(Symbols.link, size: 16),
SelectableText(data.url),
],
).opacity(0.85),
],
),
error: (err, _) => Text(err.toString()),
loading: () => CircularProgressIndicator().center(),
)
.padding(horizontal: 24, vertical: 24),
const Divider(height: 1),
// Articles list
Expanded(
child: PagingHelperView(
provider: marketplaceWebFeedContentNotifierProvider(id),
futureRefreshable:
marketplaceWebFeedContentNotifierProvider(id).future,
notifierRefreshable:
marketplaceWebFeedContentNotifierProvider(id).notifier,
contentBuilder:
(data, widgetCount, endItemView) => ListView.separated(
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 20,
),
itemCount: widgetCount,
itemBuilder: (context, index) {
if (index == widgetCount - 1) {
return endItemView;
}
final article = data.items[index];
return WebArticleCard(article: article);
},
separatorBuilder: (context, index) => const Gap(12),
),
),
),
Container(
padding: EdgeInsets.only(
bottom: 16 + MediaQuery.of(context).padding.bottom,
left: 24,
right: 24,
top: 16,
),
color: Theme.of(context).colorScheme.surfaceContainer,
child: subscribed.when(
data:
(isSubscribed) => FilledButton.icon(
onPressed:
isSubscribed ? unsubscribeFromFeed : subscribeToFeed,
icon: Icon(
isSubscribed ? Symbols.remove_circle : Symbols.add_circle,
),
label: Text(
isSubscribed ? 'unsubscribe'.tr() : 'subscribe'.tr(),
),
),
loading:
() => const SizedBox(
height: 32,
width: 32,
child: CircularProgressIndicator(strokeWidth: 2),
),
error:
(_, _) => OutlinedButton.icon(
onPressed: subscribeToFeed,
icon: const Icon(Symbols.add_circle),
label: Text('subscribe').tr(),
),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,458 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'feed_detail.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$marketplaceWebFeedHash() =>
r'8383f94f1bc272b903c341b8d95000313b69d14c';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [marketplaceWebFeed].
@ProviderFor(marketplaceWebFeed)
const marketplaceWebFeedProvider = MarketplaceWebFeedFamily();
/// See also [marketplaceWebFeed].
class MarketplaceWebFeedFamily extends Family<AsyncValue<SnWebFeed>> {
/// See also [marketplaceWebFeed].
const MarketplaceWebFeedFamily();
/// See also [marketplaceWebFeed].
MarketplaceWebFeedProvider call(String feedId) {
return MarketplaceWebFeedProvider(feedId);
}
@override
MarketplaceWebFeedProvider getProviderOverride(
covariant MarketplaceWebFeedProvider provider,
) {
return call(provider.feedId);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'marketplaceWebFeedProvider';
}
/// See also [marketplaceWebFeed].
class MarketplaceWebFeedProvider extends AutoDisposeFutureProvider<SnWebFeed> {
/// See also [marketplaceWebFeed].
MarketplaceWebFeedProvider(String feedId)
: this._internal(
(ref) => marketplaceWebFeed(ref as MarketplaceWebFeedRef, feedId),
from: marketplaceWebFeedProvider,
name: r'marketplaceWebFeedProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$marketplaceWebFeedHash,
dependencies: MarketplaceWebFeedFamily._dependencies,
allTransitiveDependencies:
MarketplaceWebFeedFamily._allTransitiveDependencies,
feedId: feedId,
);
MarketplaceWebFeedProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.feedId,
}) : super.internal();
final String feedId;
@override
Override overrideWith(
FutureOr<SnWebFeed> Function(MarketplaceWebFeedRef provider) create,
) {
return ProviderOverride(
origin: this,
override: MarketplaceWebFeedProvider._internal(
(ref) => create(ref as MarketplaceWebFeedRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
feedId: feedId,
),
);
}
@override
AutoDisposeFutureProviderElement<SnWebFeed> createElement() {
return _MarketplaceWebFeedProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is MarketplaceWebFeedProvider && other.feedId == feedId;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, feedId.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin MarketplaceWebFeedRef on AutoDisposeFutureProviderRef<SnWebFeed> {
/// The parameter `feedId` of this provider.
String get feedId;
}
class _MarketplaceWebFeedProviderElement
extends AutoDisposeFutureProviderElement<SnWebFeed>
with MarketplaceWebFeedRef {
_MarketplaceWebFeedProviderElement(super.provider);
@override
String get feedId => (origin as MarketplaceWebFeedProvider).feedId;
}
String _$marketplaceWebFeedSubscriptionHash() =>
r'2ff06a48ed7d4236b57412ecca55e94c0a0b6330';
/// Provider for web feed subscription status
///
/// Copied from [marketplaceWebFeedSubscription].
@ProviderFor(marketplaceWebFeedSubscription)
const marketplaceWebFeedSubscriptionProvider =
MarketplaceWebFeedSubscriptionFamily();
/// Provider for web feed subscription status
///
/// Copied from [marketplaceWebFeedSubscription].
class MarketplaceWebFeedSubscriptionFamily extends Family<AsyncValue<bool>> {
/// Provider for web feed subscription status
///
/// Copied from [marketplaceWebFeedSubscription].
const MarketplaceWebFeedSubscriptionFamily();
/// Provider for web feed subscription status
///
/// Copied from [marketplaceWebFeedSubscription].
MarketplaceWebFeedSubscriptionProvider call({required String feedId}) {
return MarketplaceWebFeedSubscriptionProvider(feedId: feedId);
}
@override
MarketplaceWebFeedSubscriptionProvider getProviderOverride(
covariant MarketplaceWebFeedSubscriptionProvider provider,
) {
return call(feedId: provider.feedId);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'marketplaceWebFeedSubscriptionProvider';
}
/// Provider for web feed subscription status
///
/// Copied from [marketplaceWebFeedSubscription].
class MarketplaceWebFeedSubscriptionProvider
extends AutoDisposeFutureProvider<bool> {
/// Provider for web feed subscription status
///
/// Copied from [marketplaceWebFeedSubscription].
MarketplaceWebFeedSubscriptionProvider({required String feedId})
: this._internal(
(ref) => marketplaceWebFeedSubscription(
ref as MarketplaceWebFeedSubscriptionRef,
feedId: feedId,
),
from: marketplaceWebFeedSubscriptionProvider,
name: r'marketplaceWebFeedSubscriptionProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$marketplaceWebFeedSubscriptionHash,
dependencies: MarketplaceWebFeedSubscriptionFamily._dependencies,
allTransitiveDependencies:
MarketplaceWebFeedSubscriptionFamily._allTransitiveDependencies,
feedId: feedId,
);
MarketplaceWebFeedSubscriptionProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.feedId,
}) : super.internal();
final String feedId;
@override
Override overrideWith(
FutureOr<bool> Function(MarketplaceWebFeedSubscriptionRef provider) create,
) {
return ProviderOverride(
origin: this,
override: MarketplaceWebFeedSubscriptionProvider._internal(
(ref) => create(ref as MarketplaceWebFeedSubscriptionRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
feedId: feedId,
),
);
}
@override
AutoDisposeFutureProviderElement<bool> createElement() {
return _MarketplaceWebFeedSubscriptionProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is MarketplaceWebFeedSubscriptionProvider &&
other.feedId == feedId;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, feedId.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin MarketplaceWebFeedSubscriptionRef on AutoDisposeFutureProviderRef<bool> {
/// The parameter `feedId` of this provider.
String get feedId;
}
class _MarketplaceWebFeedSubscriptionProviderElement
extends AutoDisposeFutureProviderElement<bool>
with MarketplaceWebFeedSubscriptionRef {
_MarketplaceWebFeedSubscriptionProviderElement(super.provider);
@override
String get feedId =>
(origin as MarketplaceWebFeedSubscriptionProvider).feedId;
}
String _$marketplaceWebFeedContentNotifierHash() =>
r'25688082884cb824eeff300888ba38c9748295dc';
abstract class _$MarketplaceWebFeedContentNotifier
extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnWebArticle>> {
late final String feedId;
FutureOr<CursorPagingData<SnWebArticle>> build(String feedId);
}
/// Provider for web feed articles content
///
/// Copied from [MarketplaceWebFeedContentNotifier].
@ProviderFor(MarketplaceWebFeedContentNotifier)
const marketplaceWebFeedContentNotifierProvider =
MarketplaceWebFeedContentNotifierFamily();
/// Provider for web feed articles content
///
/// Copied from [MarketplaceWebFeedContentNotifier].
class MarketplaceWebFeedContentNotifierFamily
extends Family<AsyncValue<CursorPagingData<SnWebArticle>>> {
/// Provider for web feed articles content
///
/// Copied from [MarketplaceWebFeedContentNotifier].
const MarketplaceWebFeedContentNotifierFamily();
/// Provider for web feed articles content
///
/// Copied from [MarketplaceWebFeedContentNotifier].
MarketplaceWebFeedContentNotifierProvider call(String feedId) {
return MarketplaceWebFeedContentNotifierProvider(feedId);
}
@override
MarketplaceWebFeedContentNotifierProvider getProviderOverride(
covariant MarketplaceWebFeedContentNotifierProvider provider,
) {
return call(provider.feedId);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'marketplaceWebFeedContentNotifierProvider';
}
/// Provider for web feed articles content
///
/// Copied from [MarketplaceWebFeedContentNotifier].
class MarketplaceWebFeedContentNotifierProvider
extends
AutoDisposeAsyncNotifierProviderImpl<
MarketplaceWebFeedContentNotifier,
CursorPagingData<SnWebArticle>
> {
/// Provider for web feed articles content
///
/// Copied from [MarketplaceWebFeedContentNotifier].
MarketplaceWebFeedContentNotifierProvider(String feedId)
: this._internal(
() => MarketplaceWebFeedContentNotifier()..feedId = feedId,
from: marketplaceWebFeedContentNotifierProvider,
name: r'marketplaceWebFeedContentNotifierProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$marketplaceWebFeedContentNotifierHash,
dependencies: MarketplaceWebFeedContentNotifierFamily._dependencies,
allTransitiveDependencies:
MarketplaceWebFeedContentNotifierFamily._allTransitiveDependencies,
feedId: feedId,
);
MarketplaceWebFeedContentNotifierProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.feedId,
}) : super.internal();
final String feedId;
@override
FutureOr<CursorPagingData<SnWebArticle>> runNotifierBuild(
covariant MarketplaceWebFeedContentNotifier notifier,
) {
return notifier.build(feedId);
}
@override
Override overrideWith(MarketplaceWebFeedContentNotifier Function() create) {
return ProviderOverride(
origin: this,
override: MarketplaceWebFeedContentNotifierProvider._internal(
() => create()..feedId = feedId,
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
feedId: feedId,
),
);
}
@override
AutoDisposeAsyncNotifierProviderElement<
MarketplaceWebFeedContentNotifier,
CursorPagingData<SnWebArticle>
>
createElement() {
return _MarketplaceWebFeedContentNotifierProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is MarketplaceWebFeedContentNotifierProvider &&
other.feedId == feedId;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, feedId.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin MarketplaceWebFeedContentNotifierRef
on AutoDisposeAsyncNotifierProviderRef<CursorPagingData<SnWebArticle>> {
/// The parameter `feedId` of this provider.
String get feedId;
}
class _MarketplaceWebFeedContentNotifierProviderElement
extends
AutoDisposeAsyncNotifierProviderElement<
MarketplaceWebFeedContentNotifier,
CursorPagingData<SnWebArticle>
>
with MarketplaceWebFeedContentNotifierRef {
_MarketplaceWebFeedContentNotifierProviderElement(super.provider);
@override
String get feedId =>
(origin as MarketplaceWebFeedContentNotifierProvider).feedId;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -0,0 +1,169 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/webfeed.dart';
import 'package:island/pods/network.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'dart:async';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
part 'feed_marketplace.g.dart';
@riverpod
class MarketplaceWebFeedsNotifier extends _$MarketplaceWebFeedsNotifier
with CursorPagingNotifierMixin<SnWebFeed> {
String? _query;
@override
Future<CursorPagingData<SnWebFeed>> build({required String? query}) {
_query = query;
return fetch(cursor: null);
}
@override
Future<CursorPagingData<SnWebFeed>> fetch({required String? cursor}) async {
final client = ref.read(apiClientProvider);
final offset = cursor == null ? 0 : int.parse(cursor);
final response = await client.get(
'/sphere/feeds/explore',
queryParameters: {
'offset': offset,
'take': 20,
if (_query != null && _query!.isNotEmpty) 'query': _query,
},
);
final total = int.parse(response.headers.value('X-Total') ?? '0');
final List<dynamic> data = response.data;
final feeds = data.map((e) => SnWebFeed.fromJson(e)).toList();
final hasMore = offset + feeds.length < total;
final nextCursor = hasMore ? (offset + feeds.length).toString() : null;
return CursorPagingData(
items: feeds,
hasMore: hasMore,
nextCursor: nextCursor,
);
}
}
/// Marketplace screen for browsing web feeds.
class MarketplaceWebFeedsScreen extends HookConsumerWidget {
const MarketplaceWebFeedsScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final query = useState<String?>(null);
final searchController = useTextEditingController();
final focusNode = useFocusNode();
final debounceTimer = useState<Timer?>(null);
// Clear search when query is cleared
useEffect(() {
if (query.value == null || query.value!.isEmpty) {
searchController.clear();
}
return null;
}, [query.value]);
// Clean up timer on dispose
useEffect(() {
return () {
debounceTimer.value?.cancel();
};
}, []);
return AppScaffold(
appBar: AppBar(
title: const Text('webFeeds').tr(),
actions: const [Gap(8)],
),
body: PagingHelperView(
provider: marketplaceWebFeedsNotifierProvider(query: query.value),
futureRefreshable:
marketplaceWebFeedsNotifierProvider(query: query.value).future,
notifierRefreshable:
marketplaceWebFeedsNotifierProvider(query: query.value).notifier,
contentBuilder:
(data, widgetCount, endItemView) => Column(
children: [
// Search bar above the list
Padding(
padding: const EdgeInsets.all(16),
child: SearchBar(
elevation: WidgetStateProperty.all(4),
controller: searchController,
focusNode: focusNode,
hintText: 'search'.tr(),
leading: const Icon(Symbols.search),
padding: WidgetStateProperty.all(
const EdgeInsets.symmetric(horizontal: 24),
),
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
trailing: [
if (query.value != null && query.value!.isNotEmpty)
IconButton(
icon: const Icon(Symbols.close),
onPressed: () {
query.value = null;
searchController.clear();
focusNode.unfocus();
},
),
],
onChanged: (value) {
// Debounce search to avoid excessive API calls
debounceTimer.value?.cancel();
debounceTimer.value = Timer(
const Duration(milliseconds: 500),
() {
query.value = value.isEmpty ? null : value;
},
);
},
onSubmitted: (value) {
query.value = value.isEmpty ? null : value;
focusNode.unfocus();
},
),
),
Expanded(
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: widgetCount,
itemBuilder: (context, index) {
if (index == widgetCount - 1) {
return endItemView;
}
final feed = data.items[index];
return ListTile(
title: Text(feed.title),
subtitle: Text(feed.description ?? ''),
trailing: const Icon(Symbols.chevron_right),
onTap: () {
// Navigate to web feed detail page
context.pushNamed(
'webFeedDetail',
pathParameters: {'feedId': feed.id},
);
},
);
},
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,180 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'feed_marketplace.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$marketplaceWebFeedsNotifierHash() =>
r'774b2985f2f7d61fe958f534f84e39f814327c4e';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
abstract class _$MarketplaceWebFeedsNotifier
extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnWebFeed>> {
late final String? query;
FutureOr<CursorPagingData<SnWebFeed>> build({required String? query});
}
/// See also [MarketplaceWebFeedsNotifier].
@ProviderFor(MarketplaceWebFeedsNotifier)
const marketplaceWebFeedsNotifierProvider = MarketplaceWebFeedsNotifierFamily();
/// See also [MarketplaceWebFeedsNotifier].
class MarketplaceWebFeedsNotifierFamily
extends Family<AsyncValue<CursorPagingData<SnWebFeed>>> {
/// See also [MarketplaceWebFeedsNotifier].
const MarketplaceWebFeedsNotifierFamily();
/// See also [MarketplaceWebFeedsNotifier].
MarketplaceWebFeedsNotifierProvider call({required String? query}) {
return MarketplaceWebFeedsNotifierProvider(query: query);
}
@override
MarketplaceWebFeedsNotifierProvider getProviderOverride(
covariant MarketplaceWebFeedsNotifierProvider provider,
) {
return call(query: provider.query);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'marketplaceWebFeedsNotifierProvider';
}
/// See also [MarketplaceWebFeedsNotifier].
class MarketplaceWebFeedsNotifierProvider
extends
AutoDisposeAsyncNotifierProviderImpl<
MarketplaceWebFeedsNotifier,
CursorPagingData<SnWebFeed>
> {
/// See also [MarketplaceWebFeedsNotifier].
MarketplaceWebFeedsNotifierProvider({required String? query})
: this._internal(
() => MarketplaceWebFeedsNotifier()..query = query,
from: marketplaceWebFeedsNotifierProvider,
name: r'marketplaceWebFeedsNotifierProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$marketplaceWebFeedsNotifierHash,
dependencies: MarketplaceWebFeedsNotifierFamily._dependencies,
allTransitiveDependencies:
MarketplaceWebFeedsNotifierFamily._allTransitiveDependencies,
query: query,
);
MarketplaceWebFeedsNotifierProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.query,
}) : super.internal();
final String? query;
@override
FutureOr<CursorPagingData<SnWebFeed>> runNotifierBuild(
covariant MarketplaceWebFeedsNotifier notifier,
) {
return notifier.build(query: query);
}
@override
Override overrideWith(MarketplaceWebFeedsNotifier Function() create) {
return ProviderOverride(
origin: this,
override: MarketplaceWebFeedsNotifierProvider._internal(
() => create()..query = query,
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
query: query,
),
);
}
@override
AutoDisposeAsyncNotifierProviderElement<
MarketplaceWebFeedsNotifier,
CursorPagingData<SnWebFeed>
>
createElement() {
return _MarketplaceWebFeedsNotifierProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is MarketplaceWebFeedsNotifierProvider && other.query == query;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, query.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin MarketplaceWebFeedsNotifierRef
on AutoDisposeAsyncNotifierProviderRef<CursorPagingData<SnWebFeed>> {
/// The parameter `query` of this provider.
String? get query;
}
class _MarketplaceWebFeedsNotifierProviderElement
extends
AutoDisposeAsyncNotifierProviderElement<
MarketplaceWebFeedsNotifier,
CursorPagingData<SnWebFeed>
>
with MarketplaceWebFeedsNotifierRef {
_MarketplaceWebFeedsNotifierProviderElement(super.provider);
@override
String? get query => (origin as MarketplaceWebFeedsNotifierProvider).query;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -11,6 +11,7 @@ import 'package:island/models/realm.dart';
import 'package:island/models/webfeed.dart'; import 'package:island/models/webfeed.dart';
import 'package:island/pods/event_calendar.dart'; import 'package:island/pods/event_calendar.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/pods/userinfo.dart';
import 'package:island/screens/notification.dart';
import 'package:island/services/responsive.dart'; import 'package:island/services/responsive.dart';
import 'package:island/widgets/account/fortune_graph.dart'; import 'package:island/widgets/account/fortune_graph.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
@@ -30,6 +31,33 @@ import 'package:styled_widget/styled_widget.dart';
part 'explore.g.dart'; part 'explore.g.dart';
Widget notificationIndicatorWidget(
BuildContext context, {
required int count,
EdgeInsets? margin,
}) => Card(
margin: margin,
child: ListTile(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8)),
),
leading: const Icon(Symbols.notifications),
title: Row(
children: [
Text('notifications').tr().fontSize(14),
const Gap(8),
Badge(label: Text(count.toString())),
],
),
trailing: const Icon(Symbols.chevron_right),
minTileHeight: 40,
contentPadding: EdgeInsets.only(left: 16, right: 15),
onTap: () {
GoRouter.of(context).pushNamed('notifications');
},
),
);
class ExploreScreen extends HookConsumerWidget { class ExploreScreen extends HookConsumerWidget {
const ExploreScreen({super.key}); const ExploreScreen({super.key});
@@ -77,6 +105,10 @@ class ExploreScreen extends HookConsumerWidget {
final user = ref.watch(userInfoProvider); final user = ref.watch(userInfoProvider);
final notificationCount = ref.watch(
notificationUnreadCountNotifierProvider,
);
return AppScaffold( return AppScaffold(
isNoBackground: false, isNoBackground: false,
appBar: AppBar( appBar: AppBar(
@@ -141,12 +173,60 @@ class ExploreScreen extends HookConsumerWidget {
), ),
tooltip: 'webArticlesStand'.tr(), tooltip: 'webArticlesStand'.tr(),
), ),
IconButton( PopupMenuButton(
onPressed: () { itemBuilder:
context.pushNamed('postSearch'); (context) => [
}, PopupMenuItem(
child: Row(
children: [
const Icon(Symbols.category),
const Gap(12),
Text('categories').tr(),
],
),
onTap: () {
context.pushNamed('postCategories');
},
),
PopupMenuItem(
child: Row(
children: [
const Icon(Symbols.label),
const Gap(12),
Text('tags').tr(),
],
),
onTap: () {
context.pushNamed('postTags');
},
),
PopupMenuItem(
child: Row(
children: [
const Icon(Symbols.shuffle),
const Gap(12),
Text('postShuffle').tr(),
],
),
onTap: () {
context.pushNamed('postShuffle');
},
),
PopupMenuItem(
child: Row(
children: [
const Icon(Symbols.search),
const Gap(12),
Text('search').tr(),
],
),
onTap: () {
context.pushNamed('postSearch');
},
),
],
icon: Icon( icon: Icon(
Symbols.search, Symbols.action_key,
color: Theme.of(context).appBarTheme.foregroundColor!, color: Theme.of(context).appBarTheme.foregroundColor!,
), ),
tooltip: 'search'.tr(), tooltip: 'search'.tr(),
@@ -185,7 +265,7 @@ class ExploreScreen extends HookConsumerWidget {
floatingActionButtonLocation: TabbedFabLocation(context), floatingActionButtonLocation: TabbedFabLocation(context),
body: Builder( body: Builder(
builder: (context) { builder: (context) {
final isWider = isWiderScreen(context); final isWide = isWideScreen(context);
final bodyView = _buildActivityList( final bodyView = _buildActivityList(
context, context,
@@ -193,40 +273,58 @@ class ExploreScreen extends HookConsumerWidget {
currentFilter.value, currentFilter.value,
); );
if (isWider) { if (isWide) {
return Row( return Row(
children: [ children: [
Flexible(flex: 3, child: bodyView.padding(left: 8)), Flexible(flex: 3, child: bodyView.padding(left: 8)),
if (user.value != null) if (user.value != null)
Flexible( Flexible(
flex: 2, flex: 2,
child: SingleChildScrollView( child: Align(
child: Column( alignment: Alignment.topCenter,
children: [ child: SingleChildScrollView(
CheckInWidget( child: Column(
margin: EdgeInsets.only( children: [
CheckInWidget(
margin: EdgeInsets.only(
left: 8,
right: 12,
top: 16,
),
onChecked: () {
ref.invalidate(
eventCalendarProvider(query.value),
);
},
),
if (notificationCount.value != null &&
notificationCount.value! > 0)
notificationIndicatorWidget(
context,
count: notificationCount.value ?? 0,
margin: EdgeInsets.only(
left: 8,
right: 12,
top: 8,
),
),
PostFeaturedList().padding(
left: 8, left: 8,
right: 12, right: 12,
top: 16, top: 8,
), ),
onChecked: () { FortuneGraphWidget(
ref.invalidate( margin: EdgeInsets.only(
eventCalendarProvider(query.value), left: 8,
); right: 12,
}, top: 8,
), ),
PostFeaturedList().padding( events: events,
left: 8, constrainWidth: true,
right: 12, onPointSelected: onDaySelected,
top: 8, ),
), ],
FortuneGraphWidget( ),
margin: EdgeInsets.only(left: 8, right: 12, top: 8),
events: events,
constrainWidth: true,
onPointSelected: onDaySelected,
),
],
), ),
), ),
) )
@@ -268,7 +366,7 @@ class ExploreScreen extends HookConsumerWidget {
activityListNotifierProvider(filter).notifier, activityListNotifierProvider(filter).notifier,
); );
final isWider = isWiderScreen(context); final isWide = isWideScreen(context);
return RefreshIndicator( return RefreshIndicator(
onRefresh: () => Future.sync(activitiesNotifier.forceRefresh), onRefresh: () => Future.sync(activitiesNotifier.forceRefresh),
@@ -283,7 +381,7 @@ class ExploreScreen extends HookConsumerWidget {
widgetCount: widgetCount, widgetCount: widgetCount,
endItemView: endItemView, endItemView: endItemView,
activitiesNotifier: activitiesNotifier, activitiesNotifier: activitiesNotifier,
contentOnly: isWider || filter != null, contentOnly: isWide || filter != null,
), ),
), ),
), ),
@@ -380,6 +478,10 @@ class _ActivityListView extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userInfoProvider); final user = ref.watch(userInfoProvider);
final notificationCount = ref.watch(
notificationUnreadCountNotifierProvider,
);
return CustomScrollView( return CustomScrollView(
slivers: [ slivers: [
SliverGap(12), SliverGap(12),
@@ -393,6 +495,14 @@ class _ActivityListView extends HookConsumerWidget {
SliverToBoxAdapter( SliverToBoxAdapter(
child: PostFeaturedList().padding(horizontal: 8, bottom: 4, top: 4), child: PostFeaturedList().padding(horizontal: 8, bottom: 4, top: 4),
), ),
if (!contentOnly && (notificationCount.value ?? 0) > 0)
SliverToBoxAdapter(
child: notificationIndicatorWidget(
context,
count: notificationCount.value ?? 0,
margin: EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
),
),
SliverList.builder( SliverList.builder(
itemCount: widgetCount, itemCount: widgetCount,
itemBuilder: (context, index) { itemBuilder: (context, index) {

View File

@@ -3,14 +3,17 @@ import 'dart:math' as math;
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart'; import 'package:island/models/account.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/pods/websocket.dart'; import 'package:island/pods/websocket.dart';
import 'package:island/route.dart'; import 'package:island/route.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/markdown.dart'; import 'package:island/widgets/content/markdown.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:relative_time/relative_time.dart'; import 'package:relative_time/relative_time.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
@@ -62,6 +65,10 @@ class NotificationUnreadCountNotifier
final current = await future; final current = await future;
state = AsyncData(math.max(current - count, 0)); state = AsyncData(math.max(current - count, 0));
} }
void clear() async {
state = AsyncData(0);
}
} }
@riverpod @riverpod
@@ -111,8 +118,28 @@ class NotificationScreen extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
Future<void> markAllRead() async {
showLoadingModal(context);
final apiClient = ref.watch(apiClientProvider);
await apiClient.post('/pusher/notifications/all/read');
if (!context.mounted) return;
hideLoadingModal(context);
ref.invalidate(notificationListNotifierProvider);
ref.watch(notificationUnreadCountNotifierProvider.notifier).clear();
}
return AppScaffold( return AppScaffold(
appBar: AppBar(title: const Text('notifications').tr()), appBar: AppBar(
leading: const PageBackButton(),
title: const Text('notifications').tr(),
actions: [
IconButton(
onPressed: markAllRead,
icon: const Icon(Symbols.mark_as_unread),
),
const Gap(8),
],
),
body: PagingHelperView( body: PagingHelperView(
provider: notificationListNotifierProvider, provider: notificationListNotifierProvider,
futureRefreshable: notificationListNotifierProvider.future, futureRefreshable: notificationListNotifierProvider.future,

View File

@@ -7,7 +7,7 @@ part of 'notification.dart';
// ************************************************************************** // **************************************************************************
String _$notificationUnreadCountNotifierHash() => String _$notificationUnreadCountNotifierHash() =>
r'd199abf0d16944587e747798399a267a790341f3'; r'0763b66bd64e5a9b7c317887e109ab367515dfa4';
/// See also [NotificationUnreadCountNotifier]. /// See also [NotificationUnreadCountNotifier].
@ProviderFor(NotificationUnreadCountNotifier) @ProviderFor(NotificationUnreadCountNotifier)

View File

@@ -89,6 +89,9 @@ class ArticleComposeScreen extends HookConsumerWidget {
}, [state]); }, [state]);
final showPreview = useState(false); final showPreview = useState(false);
final isAttachmentsExpanded = useState(
true,
); // New state for attachments section
// Initialize publisher once when data is available // Initialize publisher once when data is available
useEffect(() { useEffect(() {
@@ -297,71 +300,88 @@ class ArticleComposeScreen extends HookConsumerWidget {
valueListenable: state.attachments, valueListenable: state.attachments,
builder: (context, attachments, _) { builder: (context, attachments, _) {
if (attachments.isEmpty) return const SizedBox.shrink(); if (attachments.isEmpty) return const SizedBox.shrink();
return Column( return Theme(
crossAxisAlignment: CrossAxisAlignment.start, data: Theme.of(
children: [ context,
const Gap(16), ).copyWith(dividerColor: Colors.transparent),
Text( child: ExpansionTile(
'articleAttachmentHint'.tr(), initiallyExpanded: isAttachmentsExpanded.value,
style: Theme.of(context).textTheme.bodySmall?.copyWith( onExpansionChanged: (expanded) {
color: Theme.of(context).colorScheme.onSurfaceVariant, isAttachmentsExpanded.value = expanded;
), },
).padding(bottom: 8), collapsedBackgroundColor:
ValueListenableBuilder<Map<int, double>>( Theme.of(context).colorScheme.surfaceContainer,
valueListenable: state.attachmentProgress, title: Column(
builder: (context, progressMap, _) { crossAxisAlignment: CrossAxisAlignment.start,
return Wrap( children: [
spacing: 8, Text('attachments').tr(),
runSpacing: 8, Text(
children: [ 'articleAttachmentHint'.tr(),
for (var idx = 0; idx < attachments.length; idx++) style: Theme.of(
SizedBox( context,
width: 280, ).textTheme.bodySmall?.copyWith(
height: 280, color:
child: AttachmentPreview( Theme.of(
item: attachments[idx], context,
progress: progressMap[idx], ).colorScheme.onSurfaceVariant,
onRequestUpload: ),
() => ComposeLogic.uploadAttachment( ),
ref, ],
state,
idx,
),
onUpdate:
(value) =>
ComposeLogic.updateAttachment(
state,
value,
idx,
),
onDelete:
() => ComposeLogic.deleteAttachment(
ref,
state,
idx,
),
onMove: (delta) {
state
.attachments
.value = ComposeLogic.moveAttachment(
state.attachments.value,
idx,
delta,
);
},
onInsert:
() => ComposeLogic.insertAttachment(
ref,
state,
idx,
),
),
),
],
);
},
), ),
], children: [
ValueListenableBuilder<Map<int, double>>(
valueListenable: state.attachmentProgress,
builder: (context, progressMap, _) {
return Wrap(
runSpacing: 8,
spacing: 8,
children: [
for (
var idx = 0;
idx < attachments.length;
idx++
)
SizedBox(
width: 180,
height: 180,
child: AttachmentPreview(
isCompact: true,
item: attachments[idx],
progress: progressMap[idx],
onRequestUpload:
() => ComposeLogic.uploadAttachment(
ref,
state,
idx,
),
onUpdate:
(value) =>
ComposeLogic.updateAttachment(
state,
value,
idx,
),
onDelete:
() => ComposeLogic.deleteAttachment(
ref,
state,
idx,
),
onInsert:
() => ComposeLogic.insertAttachment(
ref,
state,
idx,
),
),
),
],
);
},
),
Gap(16),
],
),
); );
}, },
), ),

View File

@@ -0,0 +1,242 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/post_category.dart';
import 'package:island/models/post_tag.dart';
import 'package:island/pods/network.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
// Post Categories Notifier
final postCategoriesNotifierProvider = StateNotifierProvider.autoDispose<
PostCategoriesNotifier,
AsyncValue<CursorPagingData<SnPostCategory>>
>((ref) {
return PostCategoriesNotifier(ref);
});
class PostCategoriesNotifier
extends StateNotifier<AsyncValue<CursorPagingData<SnPostCategory>>> {
final AutoDisposeRef ref;
static const int _pageSize = 20;
bool _isLoading = false;
PostCategoriesNotifier(this.ref) : super(const AsyncValue.loading()) {
state = const AsyncValue.data(
CursorPagingData(items: [], hasMore: false, nextCursor: null),
);
fetch(cursor: null);
}
Future<void> fetch({String? cursor}) async {
if (_isLoading) return;
_isLoading = true;
if (cursor == null) {
state = const AsyncValue.loading();
}
try {
final client = ref.read(apiClientProvider);
final offset = cursor == null ? 0 : int.parse(cursor);
final response = await client.get(
'/sphere/posts/categories',
queryParameters: {
'offset': offset,
'take': _pageSize,
'order': 'usage',
},
);
final data = response.data as List;
final categories =
data.map((json) => SnPostCategory.fromJson(json)).toList();
final hasMore = categories.length == _pageSize;
final nextCursor =
hasMore ? (offset + categories.length).toString() : null;
state = AsyncValue.data(
CursorPagingData(
items: [...(state.value?.items ?? []), ...categories],
hasMore: hasMore,
nextCursor: nextCursor,
),
);
} catch (e, stack) {
state = AsyncValue.error(e, stack);
} finally {
_isLoading = false;
}
}
}
// Post Tags Notifier
final postTagsNotifierProvider = StateNotifierProvider.autoDispose<
PostTagsNotifier,
AsyncValue<CursorPagingData<SnPostTag>>
>((ref) {
return PostTagsNotifier(ref);
});
class PostTagsNotifier
extends StateNotifier<AsyncValue<CursorPagingData<SnPostTag>>> {
final AutoDisposeRef ref;
static const int _pageSize = 20;
bool _isLoading = false;
PostTagsNotifier(this.ref) : super(const AsyncValue.loading()) {
state = const AsyncValue.data(
CursorPagingData(items: [], hasMore: false, nextCursor: null),
);
fetch(cursor: null);
}
Future<void> fetch({String? cursor}) async {
if (_isLoading) return;
_isLoading = true;
if (cursor == null) {
state = const AsyncValue.loading();
}
try {
final client = ref.read(apiClientProvider);
final offset = cursor == null ? 0 : int.parse(cursor);
final response = await client.get(
'/sphere/posts/tags',
queryParameters: {
'offset': offset,
'take': _pageSize,
'order': 'usage',
},
);
final data = response.data as List;
final tags = data.map((json) => SnPostTag.fromJson(json)).toList();
final hasMore = tags.length == _pageSize;
final nextCursor = hasMore ? (offset + tags.length).toString() : null;
state = AsyncValue.data(
CursorPagingData(
items: [...(state.value?.items ?? []), ...tags],
hasMore: hasMore,
nextCursor: nextCursor,
),
);
} catch (e, stack) {
state = AsyncValue.error(e, stack);
} finally {
_isLoading = false;
}
}
}
class PostCategoriesListScreen extends ConsumerWidget {
const PostCategoriesListScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final categoriesState = ref.watch(postCategoriesNotifierProvider);
return AppScaffold(
appBar: AppBar(title: const Text('categories').tr()),
body: categoriesState.when(
data: (data) {
if (data.items.isEmpty) {
return const Center(child: Text('No categories found'));
}
return ListView.builder(
padding: EdgeInsets.zero,
itemCount: data.items.length + (data.hasMore ? 1 : 0),
itemBuilder: (context, index) {
if (index >= data.items.length) {
ref
.read(postCategoriesNotifierProvider.notifier)
.fetch(cursor: data.nextCursor);
return const Center(child: CircularProgressIndicator());
}
final category = data.items[index];
return ListTile(
leading: const Icon(Symbols.category),
contentPadding: EdgeInsets.symmetric(horizontal: 24),
trailing: const Icon(Symbols.chevron_right),
title: Text(category.categoryDisplayTitle),
subtitle: Text('postCount'.plural(category.usage)),
onTap: () {
context.pushNamed(
'postCategoryDetail',
pathParameters: {'slug': category.slug},
);
},
);
},
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error:
(error, stack) => ResponseErrorWidget(
error: error,
onRetry: () => ref.invalidate(postCategoriesNotifierProvider),
),
),
);
}
}
class PostTagsListScreen extends ConsumerWidget {
const PostTagsListScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final tagsState = ref.watch(postTagsNotifierProvider);
return AppScaffold(
appBar: AppBar(title: const Text('tags').tr()),
body: tagsState.when(
data: (data) {
if (data.items.isEmpty) {
return const Center(child: Text('No tags found'));
}
return ListView.builder(
padding: EdgeInsets.zero,
itemCount: data.items.length + (data.hasMore ? 1 : 0),
itemBuilder: (context, index) {
if (index >= data.items.length) {
ref
.read(postTagsNotifierProvider.notifier)
.fetch(cursor: data.nextCursor);
return const Center(child: CircularProgressIndicator());
}
final tag = data.items[index];
return ListTile(
title: Text(tag.name ?? '#${tag.slug}'),
contentPadding: EdgeInsets.symmetric(horizontal: 24),
leading: const Icon(Symbols.label),
trailing: const Icon(Symbols.chevron_right),
subtitle: Text('postCount'.plural(tag.usage)),
onTap: () {
context.pushNamed(
'postTagDetail',
pathParameters: {'slug': tag.slug},
);
},
);
},
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error:
(error, stack) => ResponseErrorWidget(
error: error,
onRetry: () => ref.invalidate(postTagsNotifierProvider),
),
),
);
}
}

View File

@@ -8,6 +8,7 @@ import 'package:island/pods/network.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/post/post_list.dart'; import 'package:island/widgets/post/post_list.dart';
import 'package:island/widgets/response.dart'; import 'package:island/widgets/response.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -27,6 +28,49 @@ Future<SnPostTag> postTag(Ref ref, String slug) async {
return SnPostTag.fromJson(resp.data); return SnPostTag.fromJson(resp.data);
} }
@riverpod
Future<bool> postCategorySubscriptionStatus(
Ref ref,
String slug,
bool isCategory,
) async {
final apiClient = ref.watch(apiClientProvider);
try {
final resp = await apiClient.get(
'/sphere/posts/${isCategory ? 'categories' : 'tags'}/$slug/subscription',
);
return resp.statusCode == 200;
} catch (_) {
return false;
}
}
Future<void> _subscribeToCategoryOrTag(
WidgetRef ref, {
required String slug,
required bool isCategory,
}) async {
final apiClient = ref.read(apiClientProvider);
await apiClient.post(
'/sphere/posts/${isCategory ? 'categories' : 'tags'}/$slug/subscribe',
);
// Invalidate the subscription status to refresh it
ref.invalidate(postCategorySubscriptionStatusProvider(slug, isCategory));
}
Future<void> _unsubscribeFromCategoryOrTag(
WidgetRef ref, {
required String slug,
required bool isCategory,
}) async {
final apiClient = ref.read(apiClientProvider);
await apiClient.post(
'/sphere/posts/${isCategory ? 'categories' : 'tags'}/$slug/unsubscribe',
);
// Invalidate the subscription status to refresh it
ref.invalidate(postCategorySubscriptionStatusProvider(slug, isCategory));
}
class PostCategoryDetailScreen extends HookConsumerWidget { class PostCategoryDetailScreen extends HookConsumerWidget {
final String slug; final String slug;
final bool isCategory; final bool isCategory;
@@ -41,6 +85,9 @@ class PostCategoryDetailScreen extends HookConsumerWidget {
final postCategory = final postCategory =
isCategory ? ref.watch(postCategoryProvider(slug)) : null; isCategory ? ref.watch(postCategoryProvider(slug)) : null;
final postTag = isCategory ? null : ref.watch(postTagProvider(slug)); final postTag = isCategory ? null : ref.watch(postTagProvider(slug));
final subscriptionStatus = ref.watch(
postCategorySubscriptionStatusProvider(slug, isCategory),
);
final postFilterTitle = final postFilterTitle =
isCategory isCategory
@@ -50,57 +97,158 @@ class PostCategoryDetailScreen extends HookConsumerWidget {
return AppScaffold( return AppScaffold(
isNoBackground: false, isNoBackground: false,
appBar: AppBar(title: Text(postFilterTitle).tr()), appBar: AppBar(title: Text(postFilterTitle).tr()),
body: Column( body: Expanded(
crossAxisAlignment: CrossAxisAlignment.start, child: CustomScrollView(
children: [ slivers: [
if (isCategory) if (isCategory)
postCategory!.when( SliverToBoxAdapter(
data: child: Center(
(category) => Column( child: ConstrainedBox(
crossAxisAlignment: CrossAxisAlignment.start, constraints: const BoxConstraints(maxWidth: 540),
children: [ child: Card(
Text(category.categoryDisplayTitle).bold().fontSize(15), margin: EdgeInsets.only(top: 8),
Text('A category'), child: postCategory!.when(
], data:
).padding(horizontal: 24, vertical: 16), (category) => Column(
error: crossAxisAlignment: CrossAxisAlignment.stretch,
(error, _) => ResponseErrorWidget( children: [
error: error, Text(
onRetry: () => ref.invalidate(postCategoryProvider(slug)), category.categoryDisplayTitle,
).bold().fontSize(15),
Text('A category'),
const Gap(8),
subscriptionStatus.when(
data:
(isSubscribed) =>
isSubscribed
? FilledButton.icon(
onPressed: () async {
await _unsubscribeFromCategoryOrTag(
ref,
slug: slug,
isCategory: isCategory,
);
},
icon: const Icon(
Symbols.remove_circle,
),
label: Text('unsubscribe'.tr()),
)
: FilledButton.icon(
onPressed: () async {
await _subscribeToCategoryOrTag(
ref,
slug: slug,
isCategory: isCategory,
);
},
icon: const Icon(
Symbols.add_circle,
),
label: Text('subscribe'.tr()),
),
error:
(error, _) => Text(
'Error loading subscription status',
),
loading:
() =>
CircularProgressIndicator().center(),
),
],
).padding(horizontal: 24, vertical: 16),
error:
(error, _) => ResponseErrorWidget(
error: error,
onRetry:
() => ref.invalidate(
postCategoryProvider(slug),
),
),
loading: () => ResponseLoadingWidget(),
),
),
), ),
loading: () => ResponseLoadingWidget(),
)
else
postTag!.when(
data:
(tag) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(tag.name ?? '#${tag.slug}').bold().fontSize(15),
Text('A tag'),
],
).padding(horizontal: 24, vertical: 16),
error:
(error, _) => ResponseErrorWidget(
error: error,
onRetry: () => ref.invalidate(postTagProvider(slug)),
),
loading: () => ResponseLoadingWidget(),
),
const Divider(height: 1),
Expanded(
child: CustomScrollView(
slivers: [
const SliverGap(4),
SliverPostList(
categories: isCategory ? [slug] : null,
tags: isCategory ? null : [slug],
), ),
SliverGap(MediaQuery.of(context).padding.bottom + 8), )
], else
SliverToBoxAdapter(
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 540),
child: Card(
margin: EdgeInsets.only(top: 8),
child: postTag!.when(
data:
(tag) => Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
tag.name ?? '#${tag.slug}',
).bold().fontSize(15),
Text('A tag'),
const Gap(8),
subscriptionStatus.when(
data:
(isSubscribed) =>
isSubscribed
? FilledButton.icon(
onPressed: () async {
await _unsubscribeFromCategoryOrTag(
ref,
slug: slug,
isCategory: isCategory,
);
},
icon: const Icon(
Symbols.add_circle,
),
label: Text('unsubscribe'.tr()),
)
: FilledButton.icon(
onPressed: () async {
await _subscribeToCategoryOrTag(
ref,
slug: slug,
isCategory: isCategory,
);
},
icon: const Icon(
Symbols.remove_circle,
),
label: Text('subscribe'.tr()),
),
error:
(error, _) => Text(
'Error loading subscription status',
),
loading:
() =>
CircularProgressIndicator().center(),
),
],
).padding(horizontal: 24, vertical: 16),
error:
(error, _) => ResponseErrorWidget(
error: error,
onRetry:
() => ref.invalidate(postTagProvider(slug)),
),
loading: () => ResponseLoadingWidget(),
),
),
),
),
),
const SliverGap(4),
SliverPostList(
categories: isCategory ? [slug] : null,
tags: isCategory ? null : [slug],
maxWidth: 540 + 16,
), ),
), SliverGap(MediaQuery.of(context).padding.bottom + 8),
], ],
),
), ),
); );
} }

View File

@@ -266,5 +266,146 @@ class _PostTagProviderElement
String get slug => (origin as PostTagProvider).slug; String get slug => (origin as PostTagProvider).slug;
} }
String _$postCategorySubscriptionStatusHash() =>
r'407dc7fcaeffc461b591b4ee2418811aa4f0a63f';
/// See also [postCategorySubscriptionStatus].
@ProviderFor(postCategorySubscriptionStatus)
const postCategorySubscriptionStatusProvider =
PostCategorySubscriptionStatusFamily();
/// See also [postCategorySubscriptionStatus].
class PostCategorySubscriptionStatusFamily extends Family<AsyncValue<bool>> {
/// See also [postCategorySubscriptionStatus].
const PostCategorySubscriptionStatusFamily();
/// See also [postCategorySubscriptionStatus].
PostCategorySubscriptionStatusProvider call(String slug, bool isCategory) {
return PostCategorySubscriptionStatusProvider(slug, isCategory);
}
@override
PostCategorySubscriptionStatusProvider getProviderOverride(
covariant PostCategorySubscriptionStatusProvider provider,
) {
return call(provider.slug, provider.isCategory);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'postCategorySubscriptionStatusProvider';
}
/// See also [postCategorySubscriptionStatus].
class PostCategorySubscriptionStatusProvider
extends AutoDisposeFutureProvider<bool> {
/// See also [postCategorySubscriptionStatus].
PostCategorySubscriptionStatusProvider(String slug, bool isCategory)
: this._internal(
(ref) => postCategorySubscriptionStatus(
ref as PostCategorySubscriptionStatusRef,
slug,
isCategory,
),
from: postCategorySubscriptionStatusProvider,
name: r'postCategorySubscriptionStatusProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$postCategorySubscriptionStatusHash,
dependencies: PostCategorySubscriptionStatusFamily._dependencies,
allTransitiveDependencies:
PostCategorySubscriptionStatusFamily._allTransitiveDependencies,
slug: slug,
isCategory: isCategory,
);
PostCategorySubscriptionStatusProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.slug,
required this.isCategory,
}) : super.internal();
final String slug;
final bool isCategory;
@override
Override overrideWith(
FutureOr<bool> Function(PostCategorySubscriptionStatusRef provider) create,
) {
return ProviderOverride(
origin: this,
override: PostCategorySubscriptionStatusProvider._internal(
(ref) => create(ref as PostCategorySubscriptionStatusRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
slug: slug,
isCategory: isCategory,
),
);
}
@override
AutoDisposeFutureProviderElement<bool> createElement() {
return _PostCategorySubscriptionStatusProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is PostCategorySubscriptionStatusProvider &&
other.slug == slug &&
other.isCategory == isCategory;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, slug.hashCode);
hash = _SystemHash.combine(hash, isCategory.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin PostCategorySubscriptionStatusRef on AutoDisposeFutureProviderRef<bool> {
/// The parameter `slug` of this provider.
String get slug;
/// The parameter `isCategory` of this provider.
bool get isCategory;
}
class _PostCategorySubscriptionStatusProviderElement
extends AutoDisposeFutureProviderElement<bool>
with PostCategorySubscriptionStatusRef {
_PostCategorySubscriptionStatusProviderElement(super.provider);
@override
String get slug => (origin as PostCategorySubscriptionStatusProvider).slug;
@override
bool get isCategory =>
(origin as PostCategorySubscriptionStatusProvider).isCategory;
}
// ignore_for_file: type=lint // ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

Some files were not shown because too many files have changed in this diff Show More