Compare commits

...

76 Commits

Author SHA1 Message Date
6a4a50b81a 🚀 Launch 3.5.0+161 (SNAPSHOT) 2026-01-09 02:02:52 +08:00
5e328b736f App shortcuts 2026-01-09 01:58:29 +08:00
d44e481a0d 💄 Optimize the two parts split avatar displaying 2026-01-09 01:41:21 +08:00
e6fb38572c App intents (aims to support Siri on iOS) (untested) 2026-01-09 01:39:08 +08:00
64c5ee0fe9 🐛 Trying to fix NSE attachment doesn't work as expected on the default types 2026-01-08 23:10:22 +08:00
9cba568e47 💄 New media post layout 2026-01-07 23:30:48 +08:00
38fc4e969f 💄 Improved pinned post 2026-01-07 22:52:46 +08:00
0e100c3e64 💄 Better pinned post view 2026-01-07 22:46:36 +08:00
735724ae4d 🐛 Fix some bugs within relationship forgot 2026-01-07 22:31:16 +08:00
6c31337fbc 🐛 Optimize the disposal check in message notifier, close #211 2026-01-07 19:46:07 +08:00
d2806fdc3a 💄 Optimize timeline cursor picking 2026-01-07 19:44:53 +08:00
4ebbda1b14 🐛 Trying to fix timeline duplicate posts, close #227 2026-01-07 01:55:10 +08:00
3e7007f58f 🐛 Trying to fix app scaffold focus conflict 2026-01-07 01:52:25 +08:00
d96629a7f1 🐛 Fix universal image still got state changes after unmounted 2026-01-07 01:48:20 +08:00
1b6ccccf32 💫 Animated profile card 2026-01-07 01:43:46 +08:00
910dafaa43 🐛 Fix rename didn't commited 2026-01-07 01:43:03 +08:00
c6fd342072 ♻️ Refactored the relationship screen according to server updates 2026-01-07 01:42:20 +08:00
757f1b880c 🐛 Fix explore pagination placeholder maxWidth 2026-01-07 01:22:28 +08:00
67805b413e 🐛 Fix regex in markdown 2026-01-06 01:08:09 +08:00
e1eb0f705e Prevent the non-user name got rendered as mention chip, close #226 2026-01-05 22:46:40 +08:00
1e8e785e5f Adjust riverpod auto retries logic 2026-01-05 22:35:52 +08:00
d9dfea6b8b 🐛 Fix creation date parsing in iOS code 2026-01-05 22:14:29 +08:00
d29f0b1170 Shuffle post iOS widget 2026-01-05 22:13:26 +08:00
479a79c7f6 Circular accessory on iOS 2026-01-04 23:03:18 +08:00
1a74f2b3e9 🚀 Launch 3.5.0+160 (SNAPSHOT) 2026-01-04 01:21:58 +08:00
97a5e951e1 💄 Optimize iOS notifications widget 2026-01-04 01:20:50 +08:00
9071ac44fe 🐛 Fix iOS widget catalog issue 2026-01-04 01:14:47 +08:00
6abee8d8bd iOS notification widget 2026-01-04 01:04:49 +08:00
8cf03683dc 💄 Better iOS widget styling 2026-01-03 21:42:30 +08:00
f34d80b7d4 🚚 Rename iOS check in widget file name to ready add more 2026-01-03 21:30:23 +08:00
b6d7e52148 iOS check in widget with notable days support 2026-01-03 21:29:35 +08:00
978b7b32fd iOS check in widget 2026-01-03 18:50:54 +08:00
35a9c9ff4b 🐛 Fix android biometric issue 2026-01-03 18:50:46 +08:00
e5cb296367 Due to server changes, rollback the meta field rename 2026-01-03 13:49:52 +08:00
cf3a2b6340 🎉 Create the iOS extension widget target 2026-01-03 13:06:10 +08:00
f568baf14d 💄 Adjust post subscription filter 2026-01-03 12:47:00 +08:00
703335429a 🐛 Fix post metadata uses wrong key to deserialize 2026-01-03 12:45:28 +08:00
188b6821a2 🚀 Launch 3.5.0+159 2026-01-02 20:46:18 +08:00
0ebbe0bd5a 💄 Optimize shuffle post 2026-01-02 20:34:09 +08:00
46a826ff86 👽 Update the articles url to new 2026-01-02 20:33:04 +08:00
1d99ac6441 💄 Adjust post detail screen 2026-01-02 19:57:16 +08:00
e2efdc4064 🐛 Fix chat input didn't count safe area 2026-01-02 19:27:17 +08:00
cba1a3884b 💄 Change EXIF info overlay alignment 2026-01-02 19:26:58 +08:00
7147ce1efa Better EXIF overlay 2026-01-02 19:22:47 +08:00
78c1a284a5 ♻️ Better image loading animation and more commonly used blurhash 2026-01-02 18:32:37 +08:00
f1f5113b01 💄 Swipe style post shuffle 2026-01-02 17:49:14 +08:00
a44552f105 🗑️ Remove unused activitypub screens 2026-01-02 17:02:04 +08:00
8c1ad94555 🐛 Fix route bug 2026-01-02 16:59:46 +08:00
84f5677260 🐛 Fix reference post displaying issue 2026-01-02 16:24:23 +08:00
aa1ffdbf10 ⬆️ Upgrade dependecies 2026-01-02 16:24:14 +08:00
c24d13461b 🐛 Fixes in chat subscribe 2026-01-02 16:14:55 +08:00
3b60fcb87c 💄 Optimize universal search 2026-01-02 16:14:24 +08:00
3605b997b1 💄 Optimize floating action button 2026-01-02 14:44:36 +08:00
800815c721 💄 Optimize unauthorized status 2026-01-02 14:33:41 +08:00
3b13a63e7b 💄 Optimize fediverse mention rendering, etc 2026-01-02 14:04:16 +08:00
81d69ce10f 🗑️ Remove unused stuff 2026-01-02 12:46:42 +08:00
c16d8a5912 🚀 Launch 3.5.0+158 (SNAPSHOT) 2026-01-02 03:13:37 +08:00
cb9eca0424 Better fediverse content 2026-01-02 03:08:50 +08:00
d7858bab67 🐛 Fix some bugs 2026-01-02 02:34:45 +08:00
5ce590029b 👽 Fix realm chat API didn't move 2026-01-02 00:15:32 +08:00
4d92dec45c 👽 Changes to API path since sphere no longer handle messages 2026-01-01 23:59:21 +08:00
0a4e797eec ♻️ Update the way to render total counts of data in UI, close #220 2026-01-01 11:44:10 +08:00
38dffa414f ♻️ Rebuilt fetching state machine 2026-01-01 11:40:28 +08:00
eea56a742e 🐛 Fix the paging refreshing issue 2026-01-01 11:10:04 +08:00
56b7ee1d69 🐛 Fix style mismatch 2026-01-01 02:44:25 +08:00
3b564f7e7f Able to render fediverse reactions (likes) 2026-01-01 02:37:05 +08:00
93d2670063 Able to manage publisher actor 2026-01-01 02:29:27 +08:00
788165ac5b 🐛 Fix poll feedback, close #224 2026-01-01 02:02:12 +08:00
ec71125fa9 Able to render offsite media 2026-01-01 02:00:09 +08:00
adb231278c Able to render fediverse posts 2026-01-01 01:47:09 +08:00
b3ae4ab36f 👽 Update the models to match API 2025-12-31 01:43:23 +08:00
3670fe0f10 👽 Update according to AP api changes 2025-12-29 01:43:00 +08:00
bb1a5155ed ActivityPub service impl basis 2025-12-29 01:01:47 +08:00
eb90dbbc5a 🐛 Fix fab 2025-12-28 17:19:27 +08:00
a73d9f8ec0 ♻️ Improve file viewing experience 2025-12-28 15:32:00 +08:00
4c8f2e3251 🐛 Fix tabs padding cause background color missing, close #221 2025-12-28 14:10:42 +08:00
253 changed files with 16060 additions and 11404 deletions

View File

@@ -17,6 +17,7 @@
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
<application
android:label="Solian"

View File

@@ -5,8 +5,9 @@ import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.sharedpreferences.LegacySharedPreferencesPlugin
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity : FlutterActivity()
class MainActivity : FlutterFragmentActivity()
{
private val CHANNEL = "dev.solsynth.solian/notifications"

File diff suppressed because it is too large Load Diff

View File

@@ -265,6 +265,9 @@
"relationshipStatusBlocked": "Blocked",
"blockUser": "Block User",
"unblockUser": "Unblock User",
"forgotRelationship": "Olvidar Relación",
"forgotRelationshipConfirm": "¿Estás seguro de que quieres olvidar tu relación con {}?",
"relationshipDeleted": "Relación eliminada",
"friendRequestAccepted": "Accepted friend request from {}",
"friendRequestDeclined": "Declined friend request from {}",
"requestExpiredIn": "Expired in {}",

View File

@@ -265,6 +265,9 @@
"relationshipStatusBlocked": "Blocked",
"blockUser": "Block User",
"unblockUser": "Unblock User",
"forgotRelationship": "関係を忘れる",
"forgotRelationshipConfirm": "{} との関係を忘れてもよろしいですか?",
"relationshipDeleted": "関係が削除されました",
"friendRequestAccepted": "Accepted friend request from {}",
"friendRequestDeclined": "Declined friend request from {}",
"requestExpiredIn": "Expired in {}",

View File

@@ -265,6 +265,9 @@
"relationshipStatusBlocked": "Blocked",
"blockUser": "Block User",
"unblockUser": "Unblock User",
"forgotRelationship": "관계 잊기",
"forgotRelationshipConfirm": "{}와의 관계를 잊으시겠습니까?",
"relationshipDeleted": "관계가 삭제되었습니다",
"friendRequestAccepted": "Accepted friend request from {}",
"friendRequestDeclined": "Declined friend request from {}",
"requestExpiredIn": "Expired in {}",

View File

@@ -265,6 +265,9 @@
"relationshipStatusBlocked": "已屏蔽",
"blockUser": "屏蔽用户",
"unblockUser": "解除屏蔽用户",
"forgotRelationship": "忘记关系",
"forgotRelationshipConfirm": "确定要忘记与 {} 的关系吗?这只会改变您对他/她的关系,他可能看到不到您的友限帖,但是您仍然可以看到他/她的。他/她也可以再次提出好友申请。",
"relationshipDeleted": "关系已删除",
"friendRequestAccepted": "已接受 {} 的好友请求",
"friendRequestDeclined": "已拒绝 {} 的好友请求",
"requestExpiredIn": "{} 后过期",
@@ -455,6 +458,7 @@
"checkInResultT2": "中平",
"checkInResultT3": "吉",
"checkInResultT4": "大吉",
"checkInResultT5": "特殊",
"accountProfileView": "查看个人资料",
"unspecified": "未指定",
"added": "已添加",

View File

@@ -265,6 +265,9 @@
"relationshipStatusBlocked": "已阻",
"blockUser": "禁此戶",
"unblockUser": "解禁",
"forgotRelationship": "忘締交",
"forgotRelationshipConfirm": "確定要忘與 {} 之締交乎?",
"relationshipDeleted": "締交已忘",
"friendRequestAccepted": "已納 {} 締交書",
"friendRequestDeclined": "已謝 {} 締交書",
"requestExpiredIn": "效期 {}",
@@ -455,6 +458,7 @@
"checkInResultT2": "Mid",
"checkInResultT3": "Good",
"checkInResultT4": "Best",
"checkInResultT5": "Special",
"accountProfileView": "View Profile",
"unspecified": "Unspecified",
"added": "Added",

View File

@@ -265,6 +265,9 @@
"relationshipStatusBlocked": "已屏蔽",
"blockUser": "屏蔽用戶",
"unblockUser": "解除屏蔽用戶",
"forgotRelationship": "忘記關係",
"forgotRelationshipConfirm": "確定要忘記與 {} 的關係嗎?",
"relationshipDeleted": "關係已刪除",
"friendRequestAccepted": "已接受 {} 的好友請求",
"friendRequestDeclined": "已拒絕 {} 的好友請求",
"requestExpiredIn": "{} 後過期",
@@ -455,6 +458,7 @@
"checkInResultT2": "中平",
"checkInResultT3": "吉",
"checkInResultT4": "大吉",
"checkInResultT5": "特殊",
"accountProfileView": "查看個人資料",
"unspecified": "未指定",
"added": "已添加",

View File

@@ -1,4 +1,4 @@
platform :ios, '15.0'
platform :ios, '16.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@@ -125,6 +125,8 @@ PODS:
- nanopb (~> 3.30910.0)
- PromisesSwift (~> 2.1)
- Flutter (1.0.0)
- flutter_app_intents (0.1.0):
- Flutter
- flutter_app_update (0.0.1):
- Flutter
- flutter_inappwebview_ios (0.0.1):
@@ -269,6 +271,8 @@ PODS:
- PromisesObjC (= 2.4.0)
- protocol_handler_ios (0.0.1):
- Flutter
- quick_actions_ios (0.0.1):
- Flutter
- receive_sharing_intent (1.8.1):
- Flutter
- record_ios (1.1.0):
@@ -336,6 +340,7 @@ DEPENDENCIES:
- firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- Flutter (from `Flutter`)
- flutter_app_intents (from `.symlinks/plugins/flutter_app_intents/ios`)
- flutter_app_update (from `.symlinks/plugins/flutter_app_update/ios`)
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
@@ -362,6 +367,7 @@ DEPENDENCIES:
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- pointer_interceptor_ios (from `.symlinks/plugins/pointer_interceptor_ios/ios`)
- protocol_handler_ios (from `.symlinks/plugins/protocol_handler_ios/ios`)
- quick_actions_ios (from `.symlinks/plugins/quick_actions_ios/ios`)
- receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`)
- record_ios (from `.symlinks/plugins/record_ios/ios`)
- sensors_plus (from `.symlinks/plugins/sensors_plus/ios`)
@@ -428,6 +434,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/firebase_messaging/ios"
Flutter:
:path: Flutter
flutter_app_intents:
:path: ".symlinks/plugins/flutter_app_intents/ios"
flutter_app_update:
:path: ".symlinks/plugins/flutter_app_update/ios"
flutter_inappwebview_ios:
@@ -476,6 +484,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/pointer_interceptor_ios/ios"
protocol_handler_ios:
:path: ".symlinks/plugins/protocol_handler_ios/ios"
quick_actions_ios:
:path: ".symlinks/plugins/quick_actions_ios/ios"
receive_sharing_intent:
:path: ".symlinks/plugins/receive_sharing_intent/ios"
record_ios:
@@ -525,6 +535,7 @@ SPEC CHECKSUMS:
FirebaseRemoteConfigInterop: 3443b8cb8fffd76bb3e03b2a84bfd3db952fcda4
FirebaseSessions: 2e8f808347e665dff3e5843f275715f07045297d
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_app_intents: e77f999f398c841ab584a1925dbce33ee0168fb5
flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
flutter_keyboard_visibility: 4625131e43015dbbe759d9b20daaf77e0e3f6619
@@ -561,6 +572,7 @@ SPEC CHECKSUMS:
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
protocol_handler_ios: 59f23ee71f3ec602d67902ca7f669a80957888d5
quick_actions_ios: 500fcc11711d9f646739093395c4ae8eec25f779
receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00
record_ios: f75fa1d57f840012775c0e93a38a7f3ceea1a374
SDWebImage: e9c98383c7572d713c1a0d7dd2783b10599b9838
@@ -578,6 +590,6 @@ SPEC CHECKSUMS:
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
WebRTC-SDK: 40d4f5ba05cadff14e4db5614aec402a633f007e
PODFILE CHECKSUM: 585198f58dca90ac6492607c83a8d17045ab3852
PODFILE CHECKSUM: 5a3652182a0f0e1093d3ac36dcabc72f6cc46b3a
COCOAPODS: 1.16.2

View File

@@ -11,6 +11,9 @@
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
5D8143680678FCD1D1827271 /* Pods_Solian_Watch_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9C046CF867AE03DC170F861 /* Pods_Solian_Watch_App.framework */; };
7301DB032F08D99C008390F3 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7301DB022F08D99C008390F3 /* WidgetKit.framework */; };
7301DB052F08D99C008390F3 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7301DB042F08D99C008390F3 /* SwiftUI.framework */; };
7301DB102F08D99D008390F3 /* SolianWidgetExtensionExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 7301DB012F08D99C008390F3 /* SolianWidgetExtensionExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
7310A7DF2EB10963002C0FD3 /* Solian Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 7310A7D42EB10962002C0FD3 /* Solian Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
73ACDFAD2E3D0E6100B63535 /* ReplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73ACDFAC2E3D0E6100B63535 /* ReplayKit.framework */; };
73ACDFC32E3D0E6100B63535 /* SolianBroadcastExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 73ACDFAB2E3D0E6100B63535 /* SolianBroadcastExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
@@ -36,6 +39,13 @@
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
7301DB0E2F08D99D008390F3 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 7301DB002F08D99C008390F3;
remoteInfo = SolianWidgetExtensionExtension;
};
73ACDFC12E3D0E6100B63535 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
@@ -77,6 +87,7 @@
dstPath = "";
dstSubfolderSpec = 13;
files = (
7301DB102F08D99D008390F3 /* SolianWidgetExtensionExtension.appex in Embed Foundation Extensions */,
73ACDFC32E3D0E6100B63535 /* SolianBroadcastExtension.appex in Embed Foundation Extensions */,
73C305D82E0BE878009035B9 /* SolianShareExtension.appex in Embed Foundation Extensions */,
73CDD6812DEC00480059D95D /* SolianNotificationService.appex in Embed Foundation Extensions */,
@@ -117,6 +128,10 @@
39FE4CC6223F0D3C0E1FFD04 /* Pods_SolianNotificationService.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SolianNotificationService.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3A1C47BD29CC6AC2587D4DBE /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
7301DB012F08D99C008390F3 /* SolianWidgetExtensionExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SolianWidgetExtensionExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
7301DB022F08D99C008390F3 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
7301DB042F08D99C008390F3 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
7301DB162F08D9A5008390F3 /* SolianWidgetExtensionExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SolianWidgetExtensionExtension.entitlements; sourceTree = "<group>"; };
7310A7D42EB10962002C0FD3 /* Solian Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Solian Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
737E920B2DB6A9FF00BE9CDB /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
73ACDFAB2E3D0E6100B63535 /* SolianBroadcastExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SolianBroadcastExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -151,6 +166,13 @@
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
7301DB142F08D99D008390F3 /* Exceptions for "SolianWidgetExtension" folder in "SolianWidgetExtensionExtension" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
);
target = 7301DB002F08D99C008390F3 /* SolianWidgetExtensionExtension */;
};
73ACDFCA2E3D0E6100B63535 /* Exceptions for "SolianBroadcastExtension" folder in "SolianBroadcastExtension" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
@@ -177,12 +199,21 @@
membershipExceptions = (
CloudFile.swift,
DataExchange.swift,
GroupDefaultSync.swift,
);
target = 73CDD6792DEC00480059D95D /* SolianNotificationService */;
};
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
7301DB062F08D99C008390F3 /* SolianWidgetExtension */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
7301DB142F08D99D008390F3 /* Exceptions for "SolianWidgetExtension" folder in "SolianWidgetExtensionExtension" target */,
);
path = SolianWidgetExtension;
sourceTree = "<group>";
};
7310A7D52EB10962002C0FD3 /* Solian Watch App */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
@@ -233,6 +264,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
7301DAFE2F08D99C008390F3 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
7301DB052F08D99C008390F3 /* SwiftUI.framework in Frameworks */,
7301DB032F08D99C008390F3 /* WidgetKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
7310A7D12EB10962002C0FD3 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -295,6 +335,8 @@
73ACDFAC2E3D0E6100B63535 /* ReplayKit.framework */,
73ACDFB82E3D0E6100B63535 /* UIKit.framework */,
C9C046CF867AE03DC170F861 /* Pods_Solian_Watch_App.framework */,
7301DB022F08D99C008390F3 /* WidgetKit.framework */,
7301DB042F08D99C008390F3 /* SwiftUI.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -341,12 +383,14 @@
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
7301DB162F08D9A5008390F3 /* SolianWidgetExtensionExtension.entitlements */,
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
73CDD67B2DEC00480059D95D /* SolianNotificationService */,
73C305CF2E0BE878009035B9 /* SolianShareExtension */,
73ACDFAE2E3D0E6100B63535 /* SolianBroadcastExtension */,
7310A7D52EB10962002C0FD3 /* Solian Watch App */,
7301DB062F08D99C008390F3 /* SolianWidgetExtension */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
91E124CE95BCB4DCD890160D /* Pods */,
@@ -364,6 +408,7 @@
73C305CE2E0BE878009035B9 /* SolianShareExtension.appex */,
73ACDFAB2E3D0E6100B63535 /* SolianBroadcastExtension.appex */,
7310A7D42EB10962002C0FD3 /* Solian Watch App.app */,
7301DB012F08D99C008390F3 /* SolianWidgetExtensionExtension.appex */,
);
name = Products;
sourceTree = "<group>";
@@ -408,6 +453,26 @@
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
7301DB002F08D99C008390F3 /* SolianWidgetExtensionExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = 7301DB152F08D99D008390F3 /* Build configuration list for PBXNativeTarget "SolianWidgetExtensionExtension" */;
buildPhases = (
7301DAFD2F08D99C008390F3 /* Sources */,
7301DAFE2F08D99C008390F3 /* Frameworks */,
7301DAFF2F08D99C008390F3 /* Resources */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
7301DB062F08D99C008390F3 /* SolianWidgetExtension */,
);
name = SolianWidgetExtensionExtension;
productName = SolianWidgetExtensionExtension;
productReference = 7301DB012F08D99C008390F3 /* SolianWidgetExtensionExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
7310A7D32EB10962002C0FD3 /* Solian Watch App */ = {
isa = PBXNativeTarget;
buildConfigurationList = 7310A7E32EB10963002C0FD3 /* Build configuration list for PBXNativeTarget "Solian Watch App" */;
@@ -515,6 +580,7 @@
73CDD6802DEC00480059D95D /* PBXTargetDependency */,
73C305D72E0BE878009035B9 /* PBXTargetDependency */,
73ACDFC22E3D0E6100B63535 /* PBXTargetDependency */,
7301DB0F2F08D99D008390F3 /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
73268D272DEB012A0076E970 /* Services */,
@@ -531,7 +597,7 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 2600;
LastSwiftUpdateCheck = 2620;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
@@ -539,6 +605,9 @@
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
7301DB002F08D99C008390F3 = {
CreatedOnToolsVersion = 26.2;
};
7310A7D32EB10962002C0FD3 = {
CreatedOnToolsVersion = 26.0.1;
};
@@ -563,6 +632,11 @@
knownRegions = (
en,
Base,
"zh-Hans",
es,
ja,
ko,
"zh-Hant",
);
mainGroup = 97C146E51CF9000F007C117D;
preferredProjectObjectVersion = 77;
@@ -576,6 +650,7 @@
73C305CD2E0BE878009035B9 /* SolianShareExtension */,
73ACDFAA2E3D0E6100B63535 /* SolianBroadcastExtension */,
7310A7D32EB10962002C0FD3 /* Solian Watch App */,
7301DB002F08D99C008390F3 /* SolianWidgetExtensionExtension */,
);
};
/* End PBXProject section */
@@ -588,6 +663,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
7301DAFF2F08D99C008390F3 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
7310A7D22EB10962002C0FD3 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -852,6 +934,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
7301DAFD2F08D99C008390F3 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
7310A7D02EB10962002C0FD3 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -898,6 +987,11 @@
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
};
7301DB0F2F08D99D008390F3 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 7301DB002F08D99C008390F3 /* SolianWidgetExtensionExtension */;
targetProxy = 7301DB0E2F08D99D008390F3 /* PBXContainerItemProxy */;
};
73ACDFC22E3D0E6100B63535 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 73ACDFAA2E3D0E6100B63535 /* SolianBroadcastExtension */;
@@ -1002,7 +1096,7 @@
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Solian;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1080,6 +1174,138 @@
};
name = Profile;
};
7301DB112F08D99D008390F3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = SolianWidgetExtensionExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = W7HPZ53V6B;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = SolianWidgetExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SolianWidgetExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolianWidgetExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
7301DB122F08D99D008390F3 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = SolianWidgetExtensionExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = W7HPZ53V6B;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = SolianWidgetExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SolianWidgetExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolianWidgetExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
7301DB132F08D99D008390F3 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = SolianWidgetExtensionExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = W7HPZ53V6B;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = SolianWidgetExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SolianWidgetExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian.SolianWidgetExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Profile;
};
7310A7E02EB10963002C0FD3 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 31EA49B10397BD4145AD765E /* Pods-Solian Watch App.debug.xcconfig */;
@@ -1371,7 +1597,7 @@
INFOPLIST_FILE = SolianShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SolianShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1416,7 +1642,7 @@
INFOPLIST_FILE = SolianShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SolianShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1458,7 +1684,7 @@
INFOPLIST_FILE = SolianShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SolianShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1724,7 +1950,7 @@
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Solian;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1755,7 +1981,7 @@
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Solian;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1785,6 +2011,16 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
7301DB152F08D99D008390F3 /* Build configuration list for PBXNativeTarget "SolianWidgetExtensionExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7301DB112F08D99D008390F3 /* Debug */,
7301DB122F08D99D008390F3 /* Release */,
7301DB132F08D99D008390F3 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
7310A7E32EB10963002C0FD3 /* Build configuration list for PBXNativeTarget "Solian Watch App" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7310A7D32EB10962002C0FD3"
BuildableName = "Solian Watch App.app"
BlueprintName = "Solian Watch App"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7310A7D32EB10962002C0FD3"
BuildableName = "Solian Watch App.app"
BlueprintName = "Solian Watch App"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7310A7D32EB10962002C0FD3"
BuildableName = "Solian Watch App.app"
BlueprintName = "Solian Watch App"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2620"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "73ACDFAA2E3D0E6100B63535"
BuildableName = "SolianBroadcastExtension.appex"
BlueprintName = "SolianBroadcastExtension"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2620"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "73CDD6792DEC00480059D95D"
BuildableName = "SolianNotificationService.appex"
BlueprintName = "SolianNotificationService"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2620"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "73C305CD2E0BE878009035B9"
BuildableName = "SolianShareExtension.appex"
BlueprintName = "SolianShareExtension"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2620"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7301DB002F08D99C008390F3"
BuildableName = "SolianWidgetExtensionExtension.appex"
BlueprintName = "SolianWidgetExtensionExtension"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7310A7D32EB10962002C0FD3"
BuildableName = "Solian Watch App.app"
BlueprintName = "Solian Watch App"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "_XCWidgetKind"
value = ""
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "_XCWidgetDefaultView"
value = "timeline"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "_XCWidgetFamily"
value = "systemMedium"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,6 +1,9 @@
import Flutter
import WidgetKit
import UIKit
import WatchConnectivity
import AppIntents
import flutter_app_intents
@main
@objc class AppDelegate: FlutterAppDelegate {
@@ -11,6 +14,9 @@ import WatchConnectivity
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
syncDefaultsToGroup()
WidgetCenter.shared.reloadAllTimelines()
UNUserNotificationCenter.current().delegate = notifyDelegate
let replyableMessageCategory = UNNotificationCategory(
@@ -29,6 +35,9 @@ import WatchConnectivity
GeneratedPluginRegistrant.register(with: self)
// Setup widget sync method channel
setupWidgetSyncChannel()
// Always initialize and retain a strong reference
if WCSession.isSupported() {
AppDelegate.sharedWatchConnectivityService = WatchConnectivityService.shared
@@ -38,6 +47,30 @@ import WatchConnectivity
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func setupWidgetSyncChannel() {
let controller = window?.rootViewController as? FlutterViewController
let channel = FlutterMethodChannel(name: "dev.solsynth.solian/widget", binaryMessenger: controller!.binaryMessenger)
channel.setMethodCallHandler { [weak self] (call, result) in
if call.method == "syncToWidget" {
syncDefaultsToGroup()
WidgetCenter.shared.reloadAllTimelines()
result(true)
} else {
result(FlutterMethodNotImplemented)
}
}
}
override func applicationDidEnterBackground(_ application: UIApplication) {
syncDefaultsToGroup()
WidgetCenter.shared.reloadAllTimelines()
}
override func applicationWillTerminate(_ application: UIApplication) {
syncDefaultsToGroup()
}
}
final class WatchConnectivityService: NSObject, WCSessionDelegate {
@@ -76,7 +109,7 @@ final class WatchConnectivityService: NSObject, WCSessionDelegate {
let token = UserDefaults.standard.getFlutterToken()
let serverUrl = UserDefaults.standard.getServerUrl()
var data: [String: Any] = ["serverUrl": serverUrl ?? ""]
var data: [String: Any] = ["serverUrl": serverUrl]
if let token = token {
data["token"] = token
}
@@ -94,7 +127,7 @@ final class WatchConnectivityService: NSObject, WCSessionDelegate {
let token = UserDefaults.standard.getFlutterToken()
let serverUrl = UserDefaults.standard.getServerUrl()
var data: [String: Any] = ["serverUrl": serverUrl ?? ""]
var data: [String: Any] = ["serverUrl": serverUrl]
if let token = token {
data["token"] = token
}
@@ -107,3 +140,240 @@ final class WatchConnectivityService: NSObject, WCSessionDelegate {
}
}
}
// MARK: - App Intents
@available(iOS 16.0, *)
struct OpenChatIntent: AppIntent {
static var title: LocalizedStringResource = "Open Chat"
static var description = IntentDescription("Open a specific chat room")
static var isDiscoverable = true
static var openAppWhenRun = true
@Parameter(title: "Channel ID")
var channelId: String?
func perform() async throws -> some IntentResult & ReturnsValue<String> & OpensIntent {
let plugin = FlutterAppIntentsPlugin.shared
let result = await plugin.handleIntentInvocation(
identifier: "open_chat",
parameters: ["channelId": channelId ?? ""]
)
if let success = result["success"] as? Bool, success {
let value = result["value"] as? String ?? "Chat opened"
return .result(value: value)
} else {
let errorMessage = result["error"] as? String ?? "Failed to open chat"
throw AppIntentError.executionFailed(errorMessage)
}
}
}
@available(iOS 16.0, *)
struct OpenPostIntent: AppIntent {
static var title: LocalizedStringResource = "Open Post"
static var description = IntentDescription("Open a specific post")
static var isDiscoverable = true
static var openAppWhenRun = true
@Parameter(title: "Post ID")
var postId: String?
func perform() async throws -> some IntentResult & ReturnsValue<String> & OpensIntent {
let plugin = FlutterAppIntentsPlugin.shared
let result = await plugin.handleIntentInvocation(
identifier: "open_post",
parameters: ["postId": postId ?? ""]
)
if let success = result["success"] as? Bool, success {
let value = result["value"] as? String ?? "Post opened"
return .result(value: value)
} else {
let errorMessage = result["error"] as? String ?? "Failed to open post"
throw AppIntentError.executionFailed(errorMessage)
}
}
}
@available(iOS 16.0, *)
struct OpenComposeIntent: AppIntent {
static var title: LocalizedStringResource = "Open Compose"
static var description = IntentDescription("Open compose post screen")
static var isDiscoverable = true
static var openAppWhenRun = true
func perform() async throws -> some IntentResult & ReturnsValue<String> & OpensIntent {
let plugin = FlutterAppIntentsPlugin.shared
let result = await plugin.handleIntentInvocation(
identifier: "open_compose",
parameters: [:]
)
if let success = result["success"] as? Bool, success {
let value = result["value"] as? String ?? "Compose screen opened"
return .result(value: value)
} else {
let errorMessage = result["error"] as? String ?? "Failed to open compose"
throw AppIntentError.executionFailed(errorMessage)
}
}
}
@available(iOS 16.0, *)
struct ComposePostIntent: AppIntent {
static var title: LocalizedStringResource = "Compose Post"
static var description = IntentDescription("Create a new post")
static var isDiscoverable = true
static var openAppWhenRun = true
func perform() async throws -> some IntentResult & ReturnsValue<String> & OpensIntent {
let plugin = FlutterAppIntentsPlugin.shared
let result = await plugin.handleIntentInvocation(
identifier: "compose_post",
parameters: [:]
)
if let success = result["success"] as? Bool, success {
let value = result["value"] as? String ?? "Compose screen opened"
return .result(value: value)
} else {
let errorMessage = result["error"] as? String ?? "Failed to compose post"
throw AppIntentError.executionFailed(errorMessage)
}
}
}
@available(iOS 16.0, *)
struct SearchContentIntent: AppIntent {
static var title: LocalizedStringResource = "Search Content"
static var description = IntentDescription("Search for content")
static var isDiscoverable = true
static var openAppWhenRun = true
@Parameter(title: "Search Query")
var query: String?
func perform() async throws -> some IntentResult & ReturnsValue<String> & OpensIntent {
let plugin = FlutterAppIntentsPlugin.shared
let result = await plugin.handleIntentInvocation(
identifier: "search_content",
parameters: ["query": query ?? ""]
)
if let success = result["success"] as? Bool, success {
let value = result["value"] as? String ?? "Search opened"
return .result(value: value)
} else {
let errorMessage = result["error"] as? String ?? "Failed to search"
throw AppIntentError.executionFailed(errorMessage)
}
}
}
@available(iOS 16.0, *)
struct ViewNotificationsIntent: AppIntent {
static var title: LocalizedStringResource = "View Notifications"
static var description = IntentDescription("View notifications")
static var isDiscoverable = true
static var openAppWhenRun = true
func perform() async throws -> some IntentResult & ReturnsValue<String> & OpensIntent {
let plugin = FlutterAppIntentsPlugin.shared
let result = await plugin.handleIntentInvocation(
identifier: "view_notifications",
parameters: [:]
)
if let success = result["success"] as? Bool, success {
let value = result["value"] as? String ?? "Notifications opened"
return .result(value: value)
} else {
let errorMessage = result["error"] as? String ?? "Failed to view notifications"
throw AppIntentError.executionFailed(errorMessage)
}
}
}
@available(iOS 16.0, *)
struct CheckNotificationsIntent: AppIntent {
static var title: LocalizedStringResource = "Check Notifications"
static var description = IntentDescription("Check notification count")
static var isDiscoverable = true
static var openAppWhenRun = false
func perform() async throws -> some IntentResult & ReturnsValue<String> & ProvidesDialog {
let plugin = FlutterAppIntentsPlugin.shared
let result = await plugin.handleIntentInvocation(
identifier: "check_notifications",
parameters: [:]
)
if let success = result["success"] as? Bool, success {
let value = result["value"] as? String ?? "You have new notifications"
return .result(
value: value,
dialog: "Dialog: \(value)"
)
} else {
let errorMessage = result["error"] as? String ?? "Failed to check notifications"
throw AppIntentError.executionFailed(errorMessage)
}
}
}
enum AppIntentError: Error {
case executionFailed(String)
}
@available(iOS 16.0, *)
struct AppShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
return [
// Open chat
AppShortcut(
intent: OpenChatIntent(),
phrases: [
"Open chat with \(.applicationName)",
"Go to chat using \(.applicationName)",
"Show chat in \(.applicationName)"
]
),
// Open post
AppShortcut(
intent: OpenPostIntent(),
phrases: [
"Open post with \(.applicationName)",
"Show post using \(.applicationName)"
]
),
// Compose
AppShortcut(
intent: OpenComposeIntent(),
phrases: [
"Open compose with \(.applicationName)",
"New post using \(.applicationName)",
"Write post in \(.applicationName)"
]
),
// Search
AppShortcut(
intent: SearchContentIntent(),
phrases: [
"Search in \(.applicationName)",
"Find content using \(.applicationName)"
]
),
// Check notifications
AppShortcut(
intent: CheckNotificationsIntent(),
phrases: [
"Check notifications with \(.applicationName)",
"Get notifications using \(.applicationName)",
"Do I have notifications in \(.applicationName)"
]
)
]
}
}

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -63,6 +63,18 @@
<string>Allow the Solar Network verify your ownership of the logged in account and continue your action quickly.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Grant access to Microphone will allow Solian record audio for your post.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>Solian uses speech recognition for Siri integration</string>
<key>NSAppIntentsConfiguration</key>
<dict>
<key>NSAppIntentsPackage</key>
<string>dev.solsynth.solian</string>
</dict>
<key>NSAppIntentsMetadata</key>
<dict>
<key>NSAppIntentsSupported</key>
<true/>
</dict>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Grant access to Photo Library will allow Solian download photo to album for you.</string>
<key>NSPhotoLibraryUsageDescription</key>

View File

@@ -34,7 +34,7 @@ class NotifyDelegate: UIResponder, UNUserNotificationCenterDelegate {
}
let serverUrl = UserDefaults.standard.getServerUrl()
let url = "\(serverUrl)/sphere/chat/\(metadata["room_id"] ?? "")/messages"
let url = "\(serverUrl)/messager/chat/\(metadata["room_id"] ?? "")/messages"
let parameters: [String: Any?] = [
"content": textResponse.userText,

View File

@@ -0,0 +1,41 @@
//
// GroupDefaultSync.swift
// Runner
//
// Created by LittleSheep on 2026/1/3.
//
import Foundation
private let flutterKeyPrefix = "flutter."
private let flutterKeysToSync: [String] = [
"dyn_user_tk",
"app_server_url"
]
func syncDefaultsToGroup() {
print("[iOS] syncDefaultsToGroup() called")
let standard = UserDefaults.standard
let shared = UserDefaults(suiteName: "group.solsynth.solian")
guard let shared else {
print("[iOS] App Group UserDefaults not available")
return
}
for key in flutterKeysToSync {
let prefixedKey = key.starts(with: flutterKeyPrefix) ? key : flutterKeyPrefix + key
if let value = standard.object(forKey: prefixedKey) {
print("[iOS] Syncing key to App Group: \(prefixedKey)")
shared.set(value, forKey: prefixedKey)
} else {
print("[iOS] Key \(prefixedKey) was not found in the app data, skipping...")
}
}
shared.synchronize()
print("[iOS] Sync completed")
}

View File

@@ -261,7 +261,7 @@ class NetworkService {
guard let baseURL = URL(string: serverUrl) else {
throw URLError(.badURL)
}
let url = baseURL.appendingPathComponent("/sphere/chat")
let url = baseURL.appendingPathComponent("/messager/chat")
var request = URLRequest(url: url)
request.httpMethod = "GET"
@@ -283,7 +283,7 @@ class NetworkService {
guard let baseURL = URL(string: serverUrl) else {
throw URLError(.badURL)
}
let url = baseURL.appendingPathComponent("/sphere/chat/\(identifier)")
let url = baseURL.appendingPathComponent("/messager/chat/\(identifier)")
var request = URLRequest(url: url)
request.httpMethod = "GET"
@@ -308,7 +308,7 @@ class NetworkService {
guard let baseURL = URL(string: serverUrl) else {
throw URLError(.badURL)
}
let url = baseURL.appendingPathComponent("/sphere/chat/invites")
let url = baseURL.appendingPathComponent("/messager/chat/invites")
var request = URLRequest(url: url)
request.httpMethod = "GET"
@@ -330,7 +330,7 @@ class NetworkService {
guard let baseURL = URL(string: serverUrl) else {
throw URLError(.badURL)
}
let url = baseURL.appendingPathComponent("/sphere/chat/invites/\(chatRoomId)/accept")
let url = baseURL.appendingPathComponent("/messager/chat/invites/\(chatRoomId)/accept")
var request = URLRequest(url: url)
request.httpMethod = "POST"
@@ -351,7 +351,7 @@ class NetworkService {
guard let baseURL = URL(string: serverUrl) else {
throw URLError(.badURL)
}
let url = baseURL.appendingPathComponent("/sphere/chat/invites/\(chatRoomId)/decline")
let url = baseURL.appendingPathComponent("/messager/chat/invites/\(chatRoomId)/decline")
var request = URLRequest(url: url)
request.httpMethod = "POST"
@@ -375,9 +375,9 @@ class NetworkService {
throw URLError(.badURL)
}
// Try a different pattern: /sphere/chat/messages with roomId as query param
// Try a different pattern: /messager/chat/messages with roomId as query param
var components = URLComponents(
url: baseURL.appendingPathComponent("/sphere/chat/\(chatRoomId)/messages"),
url: baseURL.appendingPathComponent("/messager/chat/\(chatRoomId)/messages"),
resolvingAgainstBaseURL: false
)!
var queryItems = [

View File

@@ -455,7 +455,7 @@ struct ChatRoomView: View {
]
// Create the URL
guard let url = URL(string: "\(serverUrl)/sphere/chat/\(room.id)/messages") else {
guard let url = URL(string: "\(serverUrl)/messager/chat/\(room.id)/messages") else {
throw URLError(.badURL)
}

View File

@@ -5,7 +5,7 @@
// Created by LittleSheep on 2025/5/31.
//
import UserNotifications
@preconcurrency import UserNotifications
import Intents
import Kingfisher
import UniformTypeIdentifiers
@@ -149,7 +149,7 @@ class NotificationService: UNNotificationServiceExtension {
let dispatchGroup = DispatchGroup()
var attachments: [UNNotificationAttachment] = []
let lock = NSLock() // To synchronize access to the attachments array
let queue = DispatchQueue(label: "dev.solsynth.solian.nse")
for attachmentUrl in attachmentUrls {
guard let remoteUrl = URL(string: attachmentUrl) else {
@@ -163,7 +163,7 @@ class NotificationService: UNNotificationServiceExtension {
.processor(scaleProcessor)
] : nil) { [weak self] result in
defer { dispatchGroup.leave() }
guard let self = self else { return }
guard self != nil else { return }
switch result {
case .success(let retrievalResult):
@@ -176,12 +176,12 @@ class NotificationService: UNNotificationServiceExtension {
try retrievalResult.image.pngData()?.write(to: cachedFileUrl)
if let attachment = try? UNNotificationAttachment(identifier: attachmentUrl, url: cachedFileUrl, options: [
UNNotificationAttachmentOptionsTypeHintKey: type?.identifier as Any,
UNNotificationAttachmentOptionsTypeHintKey: UTType.png.identifier,
UNNotificationAttachmentOptionsThumbnailHiddenKey: 0,
]) {
lock.lock()
attachments.append(attachment)
lock.unlock()
queue.async {
attachments.append(attachment)
}
}
} catch {
print("Failed to write media to temporary file: \(error.localizedDescription)")

View File

@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"images" : [
{
"filename" : "icon.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "icon-dark.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"filename" : "icon 1.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "icon.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "icon-dark.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,720 @@
//
// SolianWidgetExtension.swift
// SolianWidgetExtension
//
// Created by LittleSheep on 2026/1/3.
//
import WidgetKit
import SwiftUI
struct CheckInTip: Codable {
let isPositive: Bool
let title: String
let content: String
enum CodingKeys: String, CodingKey {
case isPositive = "is_positive"
case title
case content
}
}
struct CheckInAccount: Codable {
let id: String
let nick: String?
let profile: CheckInProfile?
}
struct CheckInProfile: Codable {
let picture: String?
}
struct CheckInResult: Codable {
let id: String
let level: Int
let rewardPoints: Int
let rewardExperience: Int
let tips: [CheckInTip]
let accountId: String
let account: CheckInAccount?
let createdAt: String
let updatedAt: String
let deletedAt: String?
enum CodingKeys: String, CodingKey {
case id
case level
case rewardPoints = "reward_points"
case rewardExperience = "reward_experience"
case tips
case accountId = "account_id"
case account
case createdAt = "created_at"
case updatedAt = "updated_at"
case deletedAt = "deleted_at"
}
var createdDate: Date? {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
if let date = formatter.date(from: createdAt) {
return date
}
// Fallback for timestamps without fractional seconds
formatter.formatOptions = [.withInternetDateTime]
return formatter.date(from: createdAt)
}
}
struct NotableDay: Codable {
let date: String
let localName: String
let globalName: String
let countryCode: String?
let localizableKey: String?
let holidays: [Int]?
enum CodingKeys: String, CodingKey {
case date
case localName = "local_name"
case globalName = "global_name"
case countryCode = "country_code"
case localizableKey = "localizable_key"
case holidays
}
var notableDate: Date? {
ISO8601DateFormatter().date(from: date)
}
var isToday: Bool {
guard let notableDate = notableDate else { return false }
let calendar = Calendar.current
return calendar.isDateInToday(notableDate)
}
}
class CheckInService {
private let networkService = WidgetNetworkService()
func fetchCheckInResult() async throws -> CheckInResult? {
return try await networkService.makeRequest(path: "/pass/accounts/me/check-in")
}
}
class NotableDayService {
private let networkService = WidgetNetworkService()
func fetchRecentNotableDay() async throws -> NotableDay? {
print("[WidgetKit] [NotableDayService] Fetching recent notable day...")
do {
let result: [NotableDay]? = try await networkService.makeRequest(path: "/pass/notable/me/recent")
print("[WidgetKit] [NotableDayService] Result: \(String(describing: result))")
guard let result = result else {
print("[WidgetKit] [NotableDayService] Result is nil")
return nil
}
print("[WidgetKit] [NotableDayService] Result count: \(result.count)")
guard result.isEmpty == false else {
print("[WidgetKit] [NotableDayService] No notable days found")
return nil
}
let firstDay = result.first!
print("[WidgetKit] [NotableDayService] First notable day: \(firstDay.localName), date: \(firstDay.date)")
return firstDay
} catch let decodingError as DecodingError {
print("[WidgetKit] [NotableDayService] Decoding error, trying as single object...")
print("[WidgetKit] [NotableDayService] Error: \(decodingError.localizedDescription)")
switch decodingError {
case .typeMismatch(let type, let context):
print("[WidgetKit] [NotableDayService] Type mismatch: expected \(type), context: \(context.debugDescription)")
case .valueNotFound(let type, let context):
print("[WidgetKit] [NotableDayService] Value not found: type \(type), context: \(context.debugDescription)")
case .keyNotFound(let key, let context):
print("[WidgetKit] [NotableDayService] Key not found: \(key), context: \(context.debugDescription)")
case .dataCorrupted(let context):
print("[WidgetKit] [NotableDayService] Data corrupted: \(context.debugDescription)")
@unknown default:
print("[WidgetKit] [NotableDayService] Unknown decoding error")
}
do {
let singleResult: NotableDay? = try await networkService.makeRequest(path: "/pass/notable/me/recent")
print("[WidgetKit] [NotableDayService] Single object decode succeeded: \(singleResult?.localName ?? "nil")")
return singleResult
} catch {
print("[WidgetKit] [NotableDayService] Single object decode also failed: \(error.localizedDescription)")
throw decodingError
}
} catch {
print("[WidgetKit] [NotableDayService] Error fetching notable day: \(error.localizedDescription)")
print("[WidgetKit] [NotableDayService] Error type: \(type(of: error))")
throw error
}
}
}
struct CheckInEntry: TimelineEntry {
let date: Date
let result: CheckInResult?
let notableDay: NotableDay?
let error: String?
let isLoading: Bool
static func placeholder() -> CheckInEntry {
CheckInEntry(date: Date(), result: nil, notableDay: nil, error: nil, isLoading: true)
}
}
struct Provider: TimelineProvider {
private let apiService = CheckInService()
func placeholder(in context: Context) -> CheckInEntry {
CheckInEntry.placeholder()
}
func getSnapshot(in context: Context, completion: @escaping (CheckInEntry) -> ()) {
Task {
print("[WidgetKit] [Provider] Getting snapshot...")
async let checkInResult = try? await apiService.fetchCheckInResult()
async let notableDay = try? await NotableDayService().fetchRecentNotableDay()
let result = try? await checkInResult
let day = try? await notableDay
print("[WidgetKit] [Provider] Snapshot - CheckIn: \(result != nil ? "Found" : "Not found"), NotableDay: \(day != nil ? "Found" : "Not found")")
let entry = CheckInEntry(date: Date(), result: result, notableDay: day, error: nil, isLoading: false)
completion(entry)
}
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
Task {
let currentDate = Date()
print("[WidgetKit] [Provider] Getting timeline at \(currentDate)...")
do {
async let checkInResult = try await apiService.fetchCheckInResult()
async let notableDay = try await NotableDayService().fetchRecentNotableDay()
let result = try await checkInResult
let day = try await notableDay
print("[WidgetKit] [Provider] Timeline - CheckIn: \(result != nil ? "Found" : "Not found"), NotableDay: \(day != nil ? "Found" : "Not found")")
let entry = CheckInEntry(date: currentDate, result: result, notableDay: day, error: nil, isLoading: false)
let nextUpdateDate: Date
if let result = result, let createdDate = result.createdDate {
let calendar = Calendar.current
if let tomorrow = calendar.date(byAdding: .day, value: 1, to: createdDate) {
nextUpdateDate = min(tomorrow, calendar.date(byAdding: .hour, value: 1, to: currentDate)!)
} else {
nextUpdateDate = calendar.date(byAdding: .hour, value: 1, to: currentDate)!
}
} else {
nextUpdateDate = Calendar.current.date(byAdding: .minute, value: 30, to: currentDate)!
}
print("[WidgetKit] [Provider] Next update at: \(nextUpdateDate)")
let timeline = Timeline(entries: [entry], policy: .after(nextUpdateDate))
completion(timeline)
} catch {
print("[WidgetKit] [Provider] Error in getTimeline: \(error.localizedDescription)")
let entry = CheckInEntry(date: currentDate, result: nil, notableDay: nil, error: error.localizedDescription, isLoading: false)
let nextUpdate = Calendar.current.date(byAdding: .minute, value: 10, to: currentDate)!
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
}
}
}
}
struct CheckInWidgetEntryView: View {
var entry: Provider.Entry
@Environment(\.widgetFamily) var family
var body: some View {
if let result = entry.result {
CheckedInView(result: result, notableDay: entry.notableDay)
} else if entry.isLoading {
LoadingView()
} else if let error = entry.error {
ErrorView(error: error)
} else {
NotCheckedInView(notableDay: entry.notableDay)
}
}
private func getLevelName(for level: Int) -> String {
let key = "checkInResultT\(level)"
return NSLocalizedString(key, comment: "Check-in result level name")
}
@ViewBuilder
private func NotableDayView(notableDay: NotableDay) -> some View {
VStack(alignment: .leading, spacing: 4) {
if !notableDay.isToday {
Text(NSLocalizedString("notableDayUpcoming", comment: "Upcoming"))
.font(.caption2)
.foregroundStyle(.secondary)
}
HStack(spacing: isAccessory ? 8 : 6) {
Image(systemName: "sparkles")
.foregroundColor(.orange)
.font(isAccessory ? .caption : .subheadline)
VStack(alignment: .leading, spacing: 2) {
if notableDay.isToday {
Text(String(format: NSLocalizedString("notableDayToday", comment: "{name} is today!"), notableDay.localName))
.font(isAccessory ? .caption : .footnote)
.fontWeight(.bold)
.foregroundColor(.primary)
.lineLimit(2)
} else {
if let notableDate = notableDay.notableDate {
let dateString = isCompact ? formatDateCompact(notableDate) : formatDateRegular(notableDate)
Text(String(format: NSLocalizedString("notableDayIs", comment: "{date} is {name}"), dateString, notableDay.localName))
.font(isAccessory ? .caption : .footnote)
.fontWeight(.semibold)
.foregroundColor(.primary)
.lineLimit(2)
} else {
Text(notableDay.localName)
.font(isAccessory ? .caption : .footnote)
.fontWeight(.semibold)
.foregroundColor(.primary)
.lineLimit(2)
}
}
}
Spacer()
}
if notableDay.isToday && !isAccessory {
HStack(spacing: 4) {
Image(systemName: "star.fill")
.font(.caption2)
.foregroundColor(.orange)
Text(NSLocalizedString("today", comment: "Today"))
.font(.caption2)
.foregroundColor(.secondary)
}
}
}
}
private var isCompact: Bool {
family == .systemSmall || isAccessory
}
private func formatDateCompact(_ date: Date) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "M/d"
return formatter.string(from: date)
}
private func formatDateRegular(_ date: Date) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "MMM d, yyyy"
return formatter.string(from: date)
}
@ViewBuilder
private func CheckedInView(result: CheckInResult, notableDay: NotableDay?) -> some View {
Link(destination: URL(string: "solian://dashboard")!) {
VStack(alignment: .leading, spacing: isAccessory ? 2 : 8) {
HStack(spacing: 8) {
Image(systemName: "flame.fill")
.foregroundColor(.orange)
.font(isAccessory ? .caption : .title3)
Text(getLevelName(for: result.level))
.font(isAccessory ? .caption2 : .headline)
.fontWeight(.bold)
Spacer()
}
if !result.tips.isEmpty {
if isAccessory {
let positiveTips = result.tips.filter { $0.isPositive }
let negativeTips = result.tips.filter { !$0.isPositive }
HStack(spacing: 2) {
if let positiveTip = positiveTips.first {
HStack(spacing: 8) {
Image(systemName: "hand.thumbsup.fill")
.font(.caption2)
.foregroundColor(.secondary)
Text(positiveTip.title)
.font(.caption2)
.foregroundColor(.primary)
.lineLimit(1)
}
}
if let negativeTip = negativeTips.first {
HStack(spacing: 8) {
Image(systemName: "hand.thumbsdown.fill")
.font(.caption2)
.foregroundColor(.secondary)
Text(negativeTip.title)
.font(.caption2)
.foregroundColor(.primary)
.lineLimit(1)
}
}
Spacer()
}
} else if family == .systemSmall {
let positiveTips = result.tips.filter { $0.isPositive }
let negativeTips = result.tips.filter { !$0.isPositive }
VStack(alignment: .leading, spacing: 4) {
if let positiveTip = positiveTips.first {
HStack(spacing: 4) {
Image(systemName: "hand.thumbsup.fill")
.font(.caption)
.foregroundColor(.green.opacity(0.8))
Text(positiveTip.title)
.font(.caption)
.foregroundColor(.primary)
Spacer()
}
}
if let negativeTip = negativeTips.first {
HStack(spacing: 4) {
Image(systemName: "hand.thumbsdown.fill")
.font(.caption)
.foregroundColor(.red.opacity(0.8))
Text(negativeTip.title)
.font(.caption)
.foregroundColor(.primary)
Spacer()
}
}
}
} else {
let positiveTips = result.tips.filter { $0.isPositive }
let negativeTips = result.tips.filter { !$0.isPositive }
VStack(alignment: .leading, spacing: 4) {
if !positiveTips.isEmpty {
HStack(spacing: 6) {
Image(systemName: "hand.thumbsup.fill")
.font(.caption)
.foregroundColor(.green.opacity(0.8))
ForEach(Array(positiveTips.prefix(3)), id: \.title) { tip in
Text(tip.title)
.font(.caption)
.foregroundColor(.primary)
if tip.title != positiveTips.last?.title {
Text("")
.font(.caption)
.foregroundColor(.secondary)
}
}
Spacer()
}
}
if !negativeTips.isEmpty {
HStack(spacing: 6) {
Image(systemName: "hand.thumbsdown.fill")
.font(.caption)
.foregroundColor(.red.opacity(0.8))
ForEach(Array(negativeTips.prefix(3)), id: \.title) { tip in
Text(tip.title)
.font(.caption)
.foregroundColor(.primary)
if tip.title != negativeTips.last?.title {
Text("")
.font(.caption)
.foregroundColor(.secondary)
}
}
Spacer()
}
}
}
}
} else if !isAccessory && family != .systemSmall {
Text("No fortune today")
.font(.caption)
.foregroundColor(.secondary)
}
if let notableDay = notableDay {
NotableDayView(notableDay: notableDay)
}
if family == .systemLarge {
Spacer()
WidgetFooter()
}
}
.padding(isAccessory ? 0 : (family == .systemSmall ? 6 : 12))
}
}
@ViewBuilder
private func WidgetFooter() -> some View {
HStack {
Text("Solian")
.font(.caption2)
.fontWeight(.medium)
.foregroundColor(.secondary)
Spacer()
}
}
private var isAccessory: Bool {
if #available(iOS 16.0, *) {
if case .accessoryRectangular = family {
return true
}
}
return false
}
@ViewBuilder
private func NotCheckedInView(notableDay: NotableDay?) -> some View {
Link(destination: URL(string: "solian://dashboard")!) {
VStack(alignment: .leading, spacing: isAccessory ? 2 : 8) {
HStack(spacing: 8) {
Image(systemName: "flame.fill")
.foregroundColor(.secondary)
.font(isAccessory ? .caption : .title3)
Text(NSLocalizedString("checkIn", comment: "Check In"))
.font(isAccessory ? .caption2 : .headline)
.fontWeight(.bold)
Spacer()
}
if !isAccessory {
Text(NSLocalizedString("tapToCheckIn", comment: "Tap to check in today"))
.font(.caption)
.foregroundColor(.secondary)
if let notableDay = notableDay {
NotableDayView(notableDay: notableDay)
}
Spacer()
WidgetFooter()
} else if let notableDay = notableDay {
NotableDayView(notableDay: notableDay)
}
}
.padding(isAccessory ? 0 : (family == .systemSmall ? 6 : 12))
}
}
@ViewBuilder
private func LoadingView() -> some View {
VStack(alignment: .leading, spacing: isAccessory ? 2 : 8) {
HStack(spacing: 4) {
ProgressView()
.scaleEffect(isAccessory ? 0.6 : 0.8)
Text(NSLocalizedString("loading", comment: "Loading..."))
.font(isAccessory ? .caption2 : .caption)
.foregroundColor(.secondary)
Spacer()
}
if !isAccessory {
Spacer()
WidgetFooter()
}
}
.padding(isAccessory ? 0 : 12)
}
@ViewBuilder
private func ErrorView(error: String) -> some View {
Link(destination: URL(string: "solian://dashboard")!) {
VStack(alignment: .leading, spacing: isAccessory ? 2 : 8) {
HStack(spacing: 4) {
Image(systemName: "exclamationmark.triangle")
.foregroundColor(.secondary)
.font(isAccessory ? .caption : .title3)
Text(NSLocalizedString("error", comment: "Error"))
.font(isAccessory ? .caption2 : .headline)
Spacer()
}
if !isAccessory {
Text(NSLocalizedString("openAppToRefresh", comment: "Open app to refresh"))
.font(.caption)
.foregroundColor(.secondary)
Text(error)
.font(.footnote)
.foregroundStyle(.secondary)
.lineLimit(nil)
.multilineTextAlignment(.leading)
Spacer()
WidgetFooter()
}
}
.padding(isAccessory ? 0 : (family == .systemSmall ? 6 : 12))
}
}
}
struct CheckInWidgetRootView: View {
var entry: Provider.Entry
@Environment(\.colorScheme) var colorScheme
var body: some View {
if #available(iOS 17.0, *) {
ZStack {
CheckInWidgetEntryView(entry: entry)
if entry.result != nil || entry.notableDay != nil {
GeometryReader { geometry in
Image(colorScheme == .dark ? "CloudyLambDark" : "CloudyLamb")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(
width: geometry.size.width * 0.9,
height: geometry.size.width * 0.9
)
.opacity(0.18)
.mask(
LinearGradient(
gradient: Gradient(colors: [
Color.white,
Color.white,
Color.clear
]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.position(
x: geometry.size.width * 0.9,
y: 20
)
}
.allowsHitTesting(false)
}
}
.containerBackground(.fill.tertiary, for: .widget)
.padding(.vertical, 8)
} else {
CheckInWidgetEntryView(entry: entry)
.padding()
.background()
}
}
}
struct SolianCheckInWidget: Widget {
let kind: String = "SolianCheckInWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
CheckInWidgetRootView(entry: entry)
}
.configurationDisplayName("Check In")
.description("View your daily check-in status")
.supportedFamilies(supportedFamilies)
}
private var supportedFamilies: [WidgetFamily] {
#if os(iOS)
return [.systemSmall, .systemMedium, .systemLarge, .accessoryRectangular]
#else
return [.systemSmall, .systemMedium, .systemLarge]
#endif
}
}
#Preview(as: .systemSmall) {
SolianCheckInWidget()
} timeline: {
CheckInEntry(date: .now, result: nil, notableDay: nil, error: nil, isLoading: false)
}
#Preview(as: .systemMedium) {
SolianCheckInWidget()
} timeline: {
CheckInEntry(
date: .now,
result: CheckInResult(
id: "test-id",
level: 2,
rewardPoints: 10,
rewardExperience: 100,
tips: [
CheckInTip(isPositive: true, title: "Good Luck", content: "Great day"),
CheckInTip(isPositive: true, title: "Creative", content: "Inspiration"),
CheckInTip(isPositive: false, title: "Shopping", content: "Expensive")
],
accountId: "account-id",
account: nil,
createdAt: ISO8601DateFormatter().string(from: Date()),
updatedAt: ISO8601DateFormatter().string(from: Date()),
deletedAt: nil
),
notableDay: NotableDay(
date: ISO8601DateFormatter().string(from: Calendar.current.date(byAdding: .day, value: 5, to: Date())!),
localName: "Christmas",
globalName: "Christmas",
countryCode: nil,
localizableKey: nil,
holidays: []
),
error: nil,
isLoading: false
)
}
#if os(iOS)
#Preview(as: .accessoryRectangular) {
SolianCheckInWidget()
} timeline: {
CheckInEntry(
date: .now,
result: CheckInResult(
id: "test-id",
level: 4,
rewardPoints: 50,
rewardExperience: 500,
tips: [
CheckInTip(isPositive: true, title: "Lucky", content: "Great fortune"),
CheckInTip(isPositive: true, title: "Success", content: "Opportunity")
],
accountId: "account-id",
account: nil,
createdAt: ISO8601DateFormatter().string(from: Date()),
updatedAt: ISO8601DateFormatter().string(from: Date()),
deletedAt: nil
),
notableDay: NotableDay(
date: ISO8601DateFormatter().string(from: Date()),
localName: "New Year",
globalName: "New Year",
countryCode: nil,
localizableKey: nil,
holidays: []
),
error: nil,
isLoading: false
)
}
#endif

View File

@@ -0,0 +1,937 @@
//
// SolianNotificationWidget.swift
// Runner
//
// Created by LittleSheep on 2026/1/4.
//
import Foundation
import WidgetKit
import SwiftUI
// MARK: - Notification Widget
struct NotificationMeta: Codable {
let pfp: String?
let images: [String]?
let actionUri: String?
enum CodingKeys: String, CodingKey {
case pfp
case images
case actionUri = "action_uri"
}
}
struct SnNotification: Codable, Identifiable {
let id: String
let topic: String
let title: String
let subtitle: String
let content: String
let meta: NotificationMeta?
let priority: Int
let viewedAt: String?
let accountId: String
let createdAt: String
let updatedAt: String
let deletedAt: String?
enum CodingKeys: String, CodingKey {
case id
case topic
case title
case subtitle
case content
case meta
case priority
case viewedAt = "viewed_at"
case accountId = "account_id"
case createdAt = "created_at"
case updatedAt = "updated_at"
case deletedAt = "deleted_at"
}
var createdDate: Date? {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
if let date = formatter.date(from: createdAt) {
return date
}
// Fallback for timestamps without fractional seconds
formatter.formatOptions = [.withInternetDateTime]
return formatter.date(from: createdAt)
}
var isUnread: Bool {
return viewedAt == nil
}
func getTopicIcon() -> String {
switch topic {
case "post.replies":
return "arrow.uturn.backward"
case "wallet.transactions":
return "wallet.pass"
case "relationships.friends.request":
return "person.badge.plus"
case "invites.chat":
return "message.badge"
case "invites.realm":
return "globe"
case "auth.login":
return "arrow.right.square"
case "posts.new":
return "doc.badge.plus"
case "wallet.orders.paid":
return "baggage"
case "posts.reactions.new":
return "face.smiling"
default:
return "bell"
}
}
}
struct NotificationEntry: TimelineEntry {
let date: Date
let notifications: [SnNotification]?
let unreadCount: Int
let error: String?
let isLoading: Bool
static func placeholder() -> NotificationEntry {
NotificationEntry(date: Date(), notifications: nil, unreadCount: 0, error: nil, isLoading: true)
}
}
class NotificationService {
private let networkService = WidgetNetworkService()
private lazy var session: URLSession = {
let configuration = URLSessionConfiguration.ephemeral
configuration.timeoutIntervalForRequest = 10.0
configuration.timeoutIntervalForResource = 10.0
configuration.waitsForConnectivity = false
return URLSession(configuration: configuration)
}()
func fetchRecentNotifications(take: Int = 5) async throws -> [SnNotification] {
if take == 0 {
return []
}
guard let token = networkService.token else {
throw RemoteError.missingCredentials
}
let baseURL = networkService.baseURL
guard let url = URL(string: "\(baseURL)/ring/notifications?unmark=true&take=\(take)") else {
throw RemoteError.invalidURL
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.timeoutInterval = 10.0
let (data, response) = try await session.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw RemoteError.invalidResponse
}
switch httpResponse.statusCode {
case 200...299:
let decoder = JSONDecoder()
let notifications = try decoder.decode([SnNotification].self, from: data)
return notifications
case 404:
return []
default:
throw RemoteError.httpError(httpResponse.statusCode)
}
}
func fetchUnreadCount() async throws -> Int {
guard let token = networkService.token else {
throw RemoteError.missingCredentials
}
let baseURL = networkService.baseURL
guard let url = URL(string: "\(baseURL)/ring/notifications/count") else {
throw RemoteError.invalidURL
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.timeoutInterval = 10.0
let (data, response) = try await session.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw RemoteError.invalidResponse
}
switch httpResponse.statusCode {
case 200...299:
if let count = try? JSONSerialization.jsonObject(with: data) as? Int {
return count
} else if let count = try? JSONSerialization.jsonObject(with: data) as? Double {
return Int(count)
}
return 0
case 404:
return 0
default:
throw RemoteError.httpError(httpResponse.statusCode)
}
}
}
struct NotificationProvider: TimelineProvider {
private let notificationService = NotificationService()
func placeholder(in context: Context) -> NotificationEntry {
NotificationEntry.placeholder()
}
func getSnapshot(in context: Context, completion: @escaping (NotificationEntry) -> ()) {
Task {
print("[WidgetKit] [NotificationProvider] Getting snapshot...")
async let notifications = try? await notificationService.fetchRecentNotifications(take: 5)
async let unreadCount = try? await notificationService.fetchUnreadCount()
let notifs = try? await notifications
let unread = (try? await unreadCount) ?? 0
print("[WidgetKit] [NotificationProvider] Snapshot - Notifications: \(notifs?.count ?? 0), Unread: \(unread)")
let entry = NotificationEntry(date: Date(), notifications: notifs, unreadCount: unread, error: nil, isLoading: false)
completion(entry)
}
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
Task {
let currentDate = Date()
print("[WidgetKit] [NotificationProvider] Getting timeline at \(currentDate)...")
do {
let takeLimit: Int
switch context.family {
case .systemSmall:
takeLimit = 0
case .systemMedium:
takeLimit = 1
case .systemLarge:
takeLimit = 3
default:
takeLimit = 0
}
async let notifications = try await notificationService.fetchRecentNotifications(take: takeLimit)
async let unreadCount = try await notificationService.fetchUnreadCount()
let notifs = try await notifications
let unread = try await unreadCount
print("[WidgetKit] [NotificationProvider] Timeline - Notifications: \(notifs.count), Unread: \(unread)")
let entry = NotificationEntry(date: currentDate, notifications: notifs, unreadCount: unread, error: nil, isLoading: false)
let nextUpdate = Calendar.current.date(byAdding: .minute, value: 15, to: currentDate)!
print("[WidgetKit] [NotificationProvider] Next update at: \(nextUpdate)")
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
} catch {
print("[WidgetKit] [NotificationProvider] Error in getTimeline: \(error.localizedDescription)")
let entry = NotificationEntry(date: currentDate, notifications: nil, unreadCount: 0, error: error.localizedDescription, isLoading: false)
let nextUpdate = Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)!
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
}
}
}
}
struct NotificationWidgetEntryView: View {
var entry: NotificationProvider.Entry
@Environment(\.widgetFamily) var family
var body: some View {
if let notifications = entry.notifications, !notifications.isEmpty {
HasNotificationsView(notifications: notifications, unreadCount: entry.unreadCount)
} else if entry.isLoading {
LoadingView()
} else if let error = entry.error {
ErrorView(error: error)
} else {
EmptyView()
}
}
private var isCompact: Bool {
family == .systemSmall || isAccessory
}
private var isAccessory: Bool {
if #available(iOS 16.0, *) {
if case .accessoryRectangular = family {
return true
}
if case .accessoryCircular = family {
return true
}
}
return false
}
private var isCircular: Bool {
if #available(iOS 16.0, *) {
if case .accessoryCircular = family {
return true
}
}
return false
}
@ViewBuilder
private func HasNotificationsView(notifications: [SnNotification], unreadCount: Int) -> some View {
Link(destination: URL(string: "solian://notifications")!) {
if isCompact {
if isAccessory {
if isCircular {
ZStack {
Image(systemName: "bell.fill")
.font(.system(size: 20))
.foregroundColor(unreadCount > 0 ? .orange : .gray)
if unreadCount > 0 {
Text("\(min(unreadCount, 99))")
.font(.system(size: 10, weight: .bold))
.foregroundColor(.white)
.padding(4)
.background(Circle().fill(Color.blue))
.offset(x: 12, y: -12)
}
}
} else {
VStack(alignment: .leading, spacing: 2) {
HStack(spacing: 4) {
Image(systemName: "bell.fill")
.font(.caption2)
.foregroundColor(.orange)
.padding(.leading, 1.5)
Text(NSLocalizedString("notifications", comment: "Notifications"))
.font(.caption2)
.fontWeight(.bold)
Spacer()
}
if unreadCount > 0 {
HStack(spacing: 4) {
Text("\(unreadCount)")
.font(.caption2)
.fontWeight(.bold)
.padding(.horizontal, 6)
.background(
Capsule()
.fill(Color.blue.opacity(0.5))
)
Text(NSLocalizedString("unread", comment: "unread"))
.font(.caption2)
}
}
Text("on the Solar Network")
.font(.caption2)
.foregroundColor(.secondary)
.padding(.horizontal, 1.5)
}
}
} else {
VStack(alignment: .leading, spacing: 8) {
HStack(spacing: 6) {
Image(systemName: "bell.fill")
.font(.subheadline)
.foregroundColor(.orange)
Text(NSLocalizedString("notifications", comment: "Notifications"))
.font(.headline)
.fontWeight(.bold)
Spacer()
}.padding(.bottom, 8)
Spacer(minLength: 2)
if unreadCount > 0 {
VStack(alignment: .leading, spacing: 2) {
Text("\(unreadCount)")
.font(.system(.largeTitle, design: .rounded))
.fontWeight(.bold)
}
}
Spacer(minLength: 2)
if unreadCount > 0 {
Text(NSLocalizedString("unreadNotifications", comment: "unread notifications"))
.font(.caption)
.foregroundColor(.secondary)
.lineLimit(2)
.multilineTextAlignment(.leading)
.fixedSize(horizontal: false, vertical: true)
} else {
Text(NSLocalizedString("noUnreadNotifications", comment: "no unread notifications"))
.font(.caption)
.foregroundColor(.secondary)
}
}
.padding(12)
}
} else {
VStack(alignment: .leading, spacing: 8) {
HStack(spacing: 6) {
Image(systemName: "bell.fill")
.font(.subheadline)
.foregroundColor(.orange)
Text(NSLocalizedString("notifications", comment: "Notifications"))
.font(.headline)
.fontWeight(.bold)
Spacer()
if unreadCount > 0 {
Text("\(unreadCount)")
.font(.caption)
.fontWeight(.bold)
.foregroundColor(.white)
.padding(.horizontal, 6)
.padding(.vertical, 2)
.background(.blue)
.clipShape(Capsule())
}
}.padding(.bottom, 8)
let displayCount = family == .systemMedium ? 1 : 5
let displayNotifications = Array(notifications.prefix(displayCount))
VStack(alignment: .leading, spacing: 4) {
ForEach(displayNotifications) { notification in
NotificationItemView(notification: notification, compact: false)
}
}
if family == .systemMedium {
Spacer()
} else {
Spacer()
Text(NSLocalizedString("tapToViewAll", comment: "Tap to view all notifications"))
.font(.caption)
.foregroundColor(.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
.padding(12)
}
}
}
@ViewBuilder
private func NotificationItemView(notification: SnNotification, compact: Bool) -> some View {
HStack(alignment: .top, spacing: compact ? 6 : 12) {
if compact {
Image(systemName: notification.getTopicIcon())
.font(.caption)
.foregroundColor(.secondary)
} else {
ZStack {
Circle()
.fill(Color.gray.opacity(0.2))
.frame(width: 32, height: 32)
Image(systemName: notification.getTopicIcon())
.font(.caption)
.foregroundColor(.primary)
}
}
VStack(alignment: .leading, spacing: 2) {
Text(notification.title)
.font(compact ? .caption : .subheadline)
.fontWeight(notification.isUnread ? .semibold : .regular)
.lineLimit(1)
if !compact && !notification.subtitle.isEmpty {
Text(notification.subtitle)
.font(.caption)
.lineLimit(1)
}
if !compact && !notification.content.isEmpty {
Text(notification.content)
.font(.caption2)
.lineLimit(2)
.multilineTextAlignment(.leading)
}
if let createdDate = notification.createdDate {
Text(formatRelativeTime(createdDate))
.font(.caption2)
.foregroundColor(.secondary)
.padding(.top, 2)
}
}
Spacer()
if notification.isUnread {
Circle()
.fill(Color.blue)
.frame(width: compact ? 6 : 8, height: compact ? 6 : 8)
.padding(.trailing, 6)
.padding(.top, 8)
}
}
.padding(.vertical, compact ? 2 : 4)
}
@ViewBuilder
private func NotificationCompactItem(notification: SnNotification) -> some View {
HStack(spacing: 4) {
Image(systemName: notification.getTopicIcon())
.font(.caption2)
.foregroundColor(.secondary)
Text(notification.title)
.font(.caption2)
.lineLimit(1)
.fontWeight(notification.isUnread ? .semibold : .regular)
Spacer()
}
}
@ViewBuilder
private func EmptyView() -> some View {
Link(destination: URL(string: "solian://notifications")!) {
if isCircular {
ZStack {
Image(systemName: "bell")
.font(.system(size: 20))
.foregroundColor(.gray)
}
} else {
VStack(alignment: .leading, spacing: isAccessory ? 4 : 8) {
HStack(spacing: 6) {
Image(systemName: "bell")
.font(isAccessory ? .caption : .title3)
.foregroundColor(.secondary)
Text(NSLocalizedString("notifications", comment: "Notifications"))
.font(isAccessory ? .caption2 : .headline)
.fontWeight(.bold)
Spacer()
}
if !isAccessory {
Text(NSLocalizedString("noNotifications", comment: "No notifications yet"))
.font(.caption)
.foregroundColor(.secondary)
Spacer()
}
}
.padding(isAccessory ? 4 : 12)
}
}
}
@ViewBuilder
private func LoadingView() -> some View {
if isCircular {
ProgressView()
.scaleEffect(0.8)
} else {
VStack(alignment: .leading, spacing: isAccessory ? 4 : 8) {
HStack(spacing: 6) {
ProgressView()
.scaleEffect(isAccessory ? 0.6 : 0.8)
Text(NSLocalizedString("loading", comment: "Loading..."))
.font(isAccessory ? .caption2 : .caption)
.foregroundColor(.secondary)
Spacer()
}
if !isAccessory {
Spacer()
}
}
.padding(isAccessory ? 4 : 12)
}
}
@ViewBuilder
private func ErrorView(error: String) -> some View {
Link(destination: URL(string: "solian://notifications")!) {
if isCircular {
ZStack {
Image(systemName: "exclamationmark.triangle")
.font(.system(size: 20))
.foregroundColor(.red)
}
} else {
VStack(alignment: .leading, spacing: isAccessory ? 4 : 8) {
HStack(spacing: 6) {
Image(systemName: "exclamationmark.triangle")
.foregroundColor(.secondary)
.font(isAccessory ? .caption : .title3)
Text(NSLocalizedString("error", comment: "Error"))
.font(isAccessory ? .caption2 : .headline)
Spacer()
}
if !isAccessory {
Text(NSLocalizedString("openAppToRefresh", comment: "Open app to refresh"))
.font(.caption)
.foregroundColor(.secondary)
Text(error)
.font(.footnote)
.foregroundStyle(.secondary)
.lineLimit(nil)
.multilineTextAlignment(.leading)
Spacer()
}
}
.padding(isAccessory ? 4 : 12)
}
}
}
private func formatRelativeTime(_ date: Date) -> String {
let now = Date()
let interval = now.timeIntervalSince(date)
if interval < 60 {
return NSLocalizedString("justNow", comment: "Just now")
} else if interval < 3600 {
let minutes = Int(interval / 60)
return String(format: NSLocalizedString("minutesAgo", comment: "%d min ago"), minutes)
} else if interval < 86400 {
let hours = Int(interval / 3600)
return String(format: NSLocalizedString("hoursAgo", comment: "%d hr ago"), hours)
} else {
let days = Int(interval / 86400)
return String(format: NSLocalizedString("daysAgo", comment: "%d d ago"), days)
}
}
}
struct NotificationWidgetRootView: View {
var entry: NotificationProvider.Entry
@Environment(\.colorScheme) var colorScheme
var body: some View {
if #available(iOS 17.0, *) {
ZStack {
NotificationWidgetEntryView(entry: entry)
if let notifications = entry.notifications, !notifications.isEmpty {
GeometryReader { geometry in
Image(colorScheme == .dark ? "CloudyLambDark" : "CloudyLamb")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(
width: geometry.size.width * 0.9,
height: geometry.size.width * 0.9
)
.opacity(0.12)
.mask(
LinearGradient(
gradient: Gradient(colors: [
Color.white,
Color.white,
Color.clear
]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.position(
x: geometry.size.width * 0.85,
y: 20
)
}
.allowsHitTesting(false)
}
}
.containerBackground(.fill.tertiary, for: .widget)
.padding(.vertical, 8)
} else {
NotificationWidgetEntryView(entry: entry)
.padding()
.background()
}
}
}
struct SolianNotificationWidget: Widget {
let kind: String = "SolianNotificationWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: NotificationProvider()) { entry in
NotificationWidgetRootView(entry: entry)
}
.configurationDisplayName("Notifications")
.description("View your recent notifications")
.supportedFamilies(supportedFamilies)
}
private var supportedFamilies: [WidgetFamily] {
#if os(iOS)
return [.systemSmall, .systemMedium, .systemLarge, .accessoryRectangular, .accessoryCircular]
#else
return [.systemSmall, .systemMedium, .systemLarge]
#endif
}
}
#if os(iOS)
#Preview(as: .accessoryRectangular) {
SolianNotificationWidget()
} timeline: {
NotificationEntry(
date: .now,
notifications: [
SnNotification(
id: "1",
topic: "post.replies",
title: "New reply to your post",
subtitle: "Someone replied to your message",
content: "This is notification content",
meta: nil,
priority: 0,
viewedAt: nil,
accountId: "acc-1",
createdAt: ISO8601DateFormatter().string(from: Date()),
updatedAt: ISO8601DateFormatter().string(from: Date()),
deletedAt: nil
),
SnNotification(
id: "2",
topic: "relationships.friends.request",
title: "New friend request",
subtitle: "You have a pending friend request",
content: "Someone wants to be your friend",
meta: nil,
priority: 0,
viewedAt: ISO8601DateFormatter().string(from: Date()),
accountId: "acc-1",
createdAt: ISO8601DateFormatter().string(from: Date().addingTimeInterval(-3600)),
updatedAt: ISO8601DateFormatter().string(from: Date()),
deletedAt: nil
)
],
unreadCount: 1,
error: nil,
isLoading: false
)
}
#endif
#Preview(as: .systemSmall) {
SolianNotificationWidget()
} timeline: {
NotificationEntry(
date: .now,
notifications: [
SnNotification(
id: "1",
topic: "post.replies",
title: "New reply to your post",
subtitle: "Someone replied to your message",
content: "This is notification content",
meta: nil,
priority: 0,
viewedAt: nil,
accountId: "acc-1",
createdAt: ISO8601DateFormatter().string(from: Date()),
updatedAt: ISO8601DateFormatter().string(from: Date()),
deletedAt: nil
),
SnNotification(
id: "2",
topic: "relationships.friends.request",
title: "New friend request",
subtitle: "You have a pending friend request",
content: "Someone wants to be your friend",
meta: nil,
priority: 0,
viewedAt: ISO8601DateFormatter().string(from: Date()),
accountId: "acc-1",
createdAt: ISO8601DateFormatter().string(from: Date().addingTimeInterval(-3600)),
updatedAt: ISO8601DateFormatter().string(from: Date()),
deletedAt: nil
)
],
unreadCount: 1,
error: nil,
isLoading: false
)
}
#Preview(as: .systemMedium) {
SolianNotificationWidget()
} timeline: {
NotificationEntry(
date: .now,
notifications: [
SnNotification(
id: "1",
topic: "post.replies",
title: "New reply to your post",
subtitle: "Someone replied to your message",
content: "This is notification content",
meta: nil,
priority: 0,
viewedAt: nil,
accountId: "acc-1",
createdAt: ISO8601DateFormatter().string(from: Date()),
updatedAt: ISO8601DateFormatter().string(from: Date()),
deletedAt: nil
),
SnNotification(
id: "2",
topic: "relationships.friends.request",
title: "New friend request",
subtitle: "You have a pending friend request",
content: "Someone wants to be your friend",
meta: nil,
priority: 0,
viewedAt: nil,
accountId: "acc-1",
createdAt: ISO8601DateFormatter().string(from: Date().addingTimeInterval(-3600)),
updatedAt: ISO8601DateFormatter().string(from: Date()),
deletedAt: nil
),
SnNotification(
id: "3",
topic: "invites.chat",
title: "New chat invite",
subtitle: "You've been invited to a chat",
content: "Join the conversation",
meta: nil,
priority: 0,
viewedAt: ISO8601DateFormatter().string(from: Date().addingTimeInterval(-86400)),
accountId: "acc-1",
createdAt: ISO8601DateFormatter().string(from: Date().addingTimeInterval(-86400)),
updatedAt: ISO8601DateFormatter().string(from: Date()),
deletedAt: nil
)
],
unreadCount: 2,
error: nil,
isLoading: false
)
}
#if os(iOS)
#Preview(as: .systemLarge) {
SolianNotificationWidget()
} timeline: {
NotificationEntry(
date: .now,
notifications: [
SnNotification(
id: "1",
topic: "post.replies",
title: "New reply",
subtitle: "Someone replied",
content: "Content",
meta: nil,
priority: 0,
viewedAt: nil,
accountId: "acc-1",
createdAt: ISO8601DateFormatter().string(from: Date()),
updatedAt: ISO8601DateFormatter().string(from: Date()),
deletedAt: nil
),
SnNotification(
id: "2",
topic: "relationships.friends.request",
title: "New friend request",
subtitle: "You have a pending friend request",
content: "Someone wants to be your friend",
meta: nil,
priority: 0,
viewedAt: nil,
accountId: "acc-1",
createdAt: ISO8601DateFormatter().string(from: Date().addingTimeInterval(-3600)),
updatedAt: ISO8601DateFormatter().string(from: Date()),
deletedAt: nil
),
SnNotification(
id: "3",
topic: "invites.chat",
title: "New chat invite",
subtitle: "You've been invited to a chat",
content: "Join the conversation",
meta: nil,
priority: 0,
viewedAt: ISO8601DateFormatter().string(from: Date().addingTimeInterval(-86400)),
accountId: "acc-1",
createdAt: ISO8601DateFormatter().string(from: Date().addingTimeInterval(-86400)),
updatedAt: ISO8601DateFormatter().string(from: Date()),
deletedAt: nil
)
],
unreadCount: 3,
error: nil,
isLoading: false
)
}
#Preview(as: .accessoryCircular) {
SolianNotificationWidget()
} timeline: {
NotificationEntry(
date: .now,
notifications: [
SnNotification(
id: "1",
topic: "post.replies",
title: "New reply",
subtitle: "Someone replied",
content: "Content",
meta: nil,
priority: 0,
viewedAt: nil,
accountId: "acc-1",
createdAt: ISO8601DateFormatter().string(from: Date()),
updatedAt: ISO8601DateFormatter().string(from: Date()),
deletedAt: nil
)
],
unreadCount: 5,
error: nil,
isLoading: false
)
}
#endif

View File

@@ -0,0 +1,549 @@
//
// SolianPostShuffleWidget.swift
// SolianWidgetExtension
//
// Created by LittleSheep on 2026/1/4.
//
import Foundation
import WidgetKit
import SwiftUI
struct SnPostPublisher: Codable {
let id: String
let name: String
let nick: String?
let description: String?
let picture: SnCloudFile?
enum CodingKeys: String, CodingKey {
case id
case name
case nick
case description
case picture
}
}
struct SnCloudFile: Codable {
let id: String
let url: String?
let thumbnail: String?
let mimeType: String?
enum CodingKeys: String, CodingKey {
case id
case url
case thumbnail
case mimeType = "mimetype"
}
}
struct SnPost: Codable, Identifiable {
let id: String
let title: String?
let description: String?
let content: String?
let publisher: SnPostPublisher
let tags: [SnPostTag]?
let createdAt: String?
let updatedAt: String?
let attachments: [SnCloudFile]?
var createdDate: Date? {
guard let createdAt = createdAt else { return nil }
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
if let date = formatter.date(from: createdAt) {
return date
}
// Fallback for timestamps without fractional seconds
formatter.formatOptions = [.withInternetDateTime]
return formatter.date(from: createdAt)
}
var hasTitle: Bool {
guard let title = title else { return false }
return !title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
}
var hasDescription: Bool {
guard let description = description else { return false }
return !description.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
}
var hasContent: Bool {
guard let content = content else { return false }
return !content.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
}
var attachmentCount: Int {
attachments?.count ?? 0
}
enum CodingKeys: String, CodingKey {
case id
case title
case description
case content
case publisher
case tags
case createdAt = "created_at"
case updatedAt = "updated_at"
case attachments
}
}
struct SnPostTag: Codable {
let id: String
let slug: String
let name: String?
}
class PostShuffleService {
private let networkService = WidgetNetworkService()
private lazy var session: URLSession = {
let configuration = URLSessionConfiguration.ephemeral
configuration.timeoutIntervalForRequest = 10.0
configuration.timeoutIntervalForResource = 10.0
configuration.waitsForConnectivity = false
return URLSession(configuration: configuration)
}()
func fetchRandomPost() async throws -> SnPost? {
guard let token = networkService.token else {
throw RemoteError.missingCredentials
}
let baseURL = networkService.baseURL
guard let url = URL(string: "\(baseURL)/sphere/posts?shuffle=true&take=1") else {
throw RemoteError.invalidURL
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.timeoutInterval = 10.0
let (data, response) = try await session.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw RemoteError.invalidResponse
}
switch httpResponse.statusCode {
case 200...299:
let decoder = JSONDecoder()
let posts = try decoder.decode([SnPost].self, from: data)
return posts.first
case 404:
return nil
default:
throw RemoteError.httpError(httpResponse.statusCode)
}
}
}
struct PostShuffleEntry: TimelineEntry {
let date: Date
let post: SnPost?
let error: String?
let isLoading: Bool
static func placeholder() -> PostShuffleEntry {
PostShuffleEntry(date: Date(), post: nil, error: nil, isLoading: true)
}
}
struct PostShuffleProvider: TimelineProvider {
private let postShuffleService = PostShuffleService()
func placeholder(in context: Context) -> PostShuffleEntry {
PostShuffleEntry.placeholder()
}
func getSnapshot(in context: Context, completion: @escaping (PostShuffleEntry) -> ()) {
Task {
print("[WidgetKit] [PostShuffleProvider] Getting snapshot...")
let post = try? await postShuffleService.fetchRandomPost()
print("[WidgetKit] [PostShuffleProvider] Snapshot - Post: \(post != nil ? "Found" : "Not found")")
let entry = PostShuffleEntry(date: Date(), post: post, error: nil, isLoading: false)
completion(entry)
}
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
Task {
let currentDate = Date()
print("[WidgetKit] [PostShuffleProvider] Getting timeline at \(currentDate)...")
do {
let post = try await postShuffleService.fetchRandomPost()
print("[WidgetKit] [PostShuffleProvider] Timeline - Post: \(post != nil ? "Found" : "Not found")")
let entry = PostShuffleEntry(date: currentDate, post: post, error: nil, isLoading: false)
let nextUpdate = Calendar.current.date(byAdding: .minute, value: 10, to: currentDate)!
print("[WidgetKit] [PostShuffleProvider] Next update at: \(nextUpdate)")
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
} catch {
print("[WidgetKit] [PostShuffleProvider] Error in getTimeline: \(error.localizedDescription)")
let entry = PostShuffleEntry(date: currentDate, post: nil, error: error.localizedDescription, isLoading: false)
let nextUpdate = Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)!
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
}
}
}
}
struct PostShuffleWidgetEntryView: View {
var entry: PostShuffleProvider.Entry
@Environment(\.widgetFamily) var family
var body: some View {
if let post = entry.post {
PostContentView(post: post)
} else if entry.isLoading {
LoadingView()
} else if let error = entry.error {
ErrorView(error: error)
} else {
EmptyView()
}
}
@ViewBuilder
private func PostContentView(post: SnPost) -> some View {
Link(destination: URL(string: "solian://posts/\(post.id)")!) {
VStack(alignment: .leading, spacing: 8) {
HStack(alignment: .top, spacing: 8) {
if let avatarUrl = post.publisher.picture?.url ?? post.publisher.picture?.thumbnail {
AsyncImage(url: URL(string: avatarUrl)) { phase in
switch phase {
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
case .failure(_):
Circle()
.fill(Color.gray.opacity(0.3))
default:
Circle()
.fill(Color.gray.opacity(0.3))
.overlay(
ProgressView()
.scaleEffect(0.5)
)
}
}
.frame(width: 40, height: 40)
.clipShape(Circle())
} else {
Circle()
.fill(Color.gray.opacity(0.3))
.frame(width: 40, height: 40)
}
VStack(alignment: .leading, spacing: 2) {
Text(post.publisher.nick ?? post.publisher.name)
.font(.headline)
.fontWeight(.semibold)
.lineLimit(1)
if let createdDate = post.createdDate {
Text(formatRelativeTime(createdDate))
.font(.caption)
.foregroundColor(.secondary)
}
}
Spacer()
}
if post.attachmentCount > 0 {
HStack(spacing: 4) {
Image(systemName: "paperclip")
.font(.caption2)
.foregroundColor(.secondary)
Text("\(post.attachmentCount)")
.font(.caption2)
.foregroundColor(.secondary)
}
}
if post.hasTitle {
Text(post.title!)
.font(family == .systemMedium ? .subheadline : .body)
.fontWeight(.semibold)
.lineLimit(family == .systemMedium ? 2 : 3)
}
if post.hasDescription {
Text(post.description!)
.font(.caption)
.foregroundColor(.secondary)
.lineLimit(family == .systemMedium ? 2 : 4)
}
if post.hasContent {
Text(post.content!)
.font(.caption)
.foregroundColor(.primary)
.lineLimit(family == .systemMedium ? 3 : 8)
}
if let tags = post.tags, !tags.isEmpty {
let displayTags = Array(tags.prefix(family == .systemMedium ? 2 : 4))
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 4) {
ForEach(displayTags, id: \.id) { tag in
Text("#\(tag.name ?? tag.slug)")
.font(.caption2)
.foregroundColor(.blue)
.padding(.horizontal, 6)
.padding(.vertical, 2)
.background(Color.blue.opacity(0.1))
.clipShape(Capsule())
}
}
}
}
Spacer()
}
.padding(12)
}
}
@ViewBuilder
private func EmptyView() -> some View {
Link(destination: URL(string: "solian://posts/shuffle")!) {
VStack(alignment: .leading, spacing: 8) {
HStack(spacing: 6) {
Image(systemName: "text.alignleft")
.font(.title3)
.foregroundColor(.secondary)
Spacer()
}
Text(NSLocalizedString("noPostsAvailable", comment: "No posts available"))
.font(.subheadline)
.foregroundColor(.secondary)
Spacer()
Text(NSLocalizedString("tapToRefresh", comment: "Tap to refresh"))
.font(.caption)
.foregroundColor(.secondary)
}
.padding(12)
}
}
@ViewBuilder
private func LoadingView() -> some View {
VStack(alignment: .leading, spacing: 8) {
HStack(spacing: 6) {
ProgressView()
.scaleEffect(0.8)
Text(NSLocalizedString("loadingPost", comment: "Loading post..."))
.font(.subheadline)
.foregroundColor(.secondary)
Spacer()
}
Spacer()
}
.padding(12)
}
@ViewBuilder
private func ErrorView(error: String) -> some View {
Link(destination: URL(string: "solian://posts/shuffle")!) {
VStack(alignment: .leading, spacing: 8) {
HStack(spacing: 6) {
Image(systemName: "exclamationmark.triangle")
.font(.title3)
.foregroundColor(.secondary)
Text(NSLocalizedString("error", comment: "Error"))
.font(.headline)
Spacer()
}
Text(NSLocalizedString("openAppToRefresh", comment: "Open app to refresh"))
.font(.caption)
.foregroundColor(.secondary)
Text(error)
.font(.footnote)
.foregroundStyle(.secondary)
.lineLimit(5)
.multilineTextAlignment(.leading)
Spacer()
}
.padding(12)
}
}
private func formatRelativeTime(_ date: Date) -> String {
let now = Date()
let interval = now.timeIntervalSince(date)
if interval < 60 {
return NSLocalizedString("justNow", comment: "Just now")
} else if interval < 3600 {
let minutes = Int(interval / 60)
return String(format: NSLocalizedString("minutesAgo", comment: "%d min ago"), minutes)
} else if interval < 86400 {
let hours = Int(interval / 3600)
return String(format: NSLocalizedString("hoursAgo", comment: "%d hr ago"), hours)
} else {
let days = Int(interval / 86400)
return String(format: NSLocalizedString("daysAgo", comment: "%d d ago"), days)
}
}
}
struct PostShuffleWidgetRootView: View {
var entry: PostShuffleProvider.Entry
@Environment(\.colorScheme) var colorScheme
var body: some View {
if #available(iOS 17.0, *) {
ZStack {
PostShuffleWidgetEntryView(entry: entry)
if entry.post != nil {
GeometryReader { geometry in
Image(colorScheme == .dark ? "CloudyLambDark" : "CloudyLamb")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(
width: geometry.size.width * 0.9,
height: geometry.size.width * 0.9
)
.opacity(0.12)
.mask(
LinearGradient(
gradient: Gradient(colors: [
Color.white,
Color.white,
Color.clear
]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.position(
x: geometry.size.width * 0.9,
y: 20
)
}
.allowsHitTesting(false)
}
}
.containerBackground(.fill.tertiary, for: .widget)
.padding(.vertical, 8)
} else {
PostShuffleWidgetEntryView(entry: entry)
.padding()
.background()
}
}
}
struct SolianPostShuffleWidget: Widget {
let kind: String = "SolianPostShuffleWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: PostShuffleProvider()) { entry in
PostShuffleWidgetRootView(entry: entry)
}
.configurationDisplayName("Random Post")
.description("Discover a random post from the network")
.supportedFamilies(supportedFamilies)
}
private var supportedFamilies: [WidgetFamily] {
return [.systemMedium, .systemLarge]
}
}
#Preview(as: .systemMedium) {
SolianPostShuffleWidget()
} timeline: {
PostShuffleEntry(
date: .now,
post: SnPost(
id: "test-post-id",
title: "Hello World!",
description: "This is a test post description",
content: "This is a content of a test post. It can be longer and show more text in the widget.",
publisher: SnPostPublisher(
id: "publisher-1",
name: "Test Publisher",
nick: "Testy",
description: "A test publisher",
picture: nil
),
tags: [
SnPostTag(id: "tag-1", slug: "test", name: "Test"),
SnPostTag(id: "tag-2", slug: "example", name: "Example")
],
createdAt: ISO8601DateFormatter().string(from: Date()),
updatedAt: ISO8601DateFormatter().string(from: Date()),
attachments: []
),
error: nil,
isLoading: false
)
}
#Preview(as: .systemLarge) {
SolianPostShuffleWidget()
} timeline: {
PostShuffleEntry(
date: .now,
post: SnPost(
id: "test-post-id",
title: "Welcome to the Solar Network!",
description: "This is a test post description that is a bit longer to demonstrate the widget layout with various content types",
content: "This is content of a test post. It can be longer and show more text in the widget. The large widget should display more content and allow for better reading experience. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
publisher: SnPostPublisher(
id: "publisher-1",
name: "Test Publisher",
nick: "Testy McTestface",
description: "A test publisher",
picture: nil
),
tags: [
SnPostTag(id: "tag-1", slug: "test", name: "Test"),
SnPostTag(id: "tag-2", slug: "example", name: "Example"),
SnPostTag(id: "tag-3", slug: "widget", name: "Widget"),
SnPostTag(id: "tag-4", slug: "ios", name: "iOS")
],
createdAt: ISO8601DateFormatter().string(from: Date()),
updatedAt: ISO8601DateFormatter().string(from: Date()),
attachments: [
SnCloudFile(id: "file-1", url: nil, thumbnail: nil, mimeType: "image/jpeg"),
SnCloudFile(id: "file-2", url: nil, thumbnail: nil, mimeType: "image/png")
]
),
error: nil,
isLoading: false
)
}

View File

@@ -0,0 +1,18 @@
//
// SolianWidgetExtensionBundle.swift
// SolianWidgetExtension
//
// Created by LittleSheep on 2026/1/3.
//
import WidgetKit
import SwiftUI
@main
struct SolianWidgetExtensionBundle: WidgetBundle {
var body: some Widget {
SolianCheckInWidget()
SolianNotificationWidget()
SolianPostShuffleWidget()
}
}

View File

@@ -0,0 +1,136 @@
//
// Networking.swift
// SolianWidgetExtensionExtension
//
// Created by LittleSheep on 2026/1/4.
//
import Foundation
enum RemoteError: Error {
case missingCredentials
case invalidURL
case invalidResponse
case httpError(Int)
case decodingError
}
extension RemoteError: LocalizedError {
var errorDescription: String? {
switch self {
case .missingCredentials:
return "Please open the app to sign in."
case .invalidURL:
return "Invalid server configuration."
case .invalidResponse:
return "Server returned an invalid response."
case .httpError(let code):
return "Server error (\(code))."
case .decodingError:
return "Failed to read server data."
}
}
}
struct TokenData: Codable {
let token: String
}
class WidgetNetworkService {
private let appGroup = "group.solsynth.solian"
private let tokenKey = "flutter.dyn_user_tk"
private let urlKey = "flutter.app_server_url"
private lazy var session: URLSession = {
let configuration = URLSessionConfiguration.ephemeral
configuration.timeoutIntervalForRequest = 10.0
configuration.timeoutIntervalForResource = 10.0
configuration.waitsForConnectivity = false
return URLSession(configuration: configuration)
}()
private var userDefaults: UserDefaults? {
UserDefaults(suiteName: appGroup)
}
var token: String? {
guard let tokenString = userDefaults?.string(forKey: tokenKey) else {
return nil
}
guard let data = tokenString.data(using: .utf8) else {
return nil
}
do {
let tokenData = try JSONDecoder().decode(TokenData.self, from: data)
return tokenData.token
} catch {
print("[WidgetKit] Failed to decode token: \(error)")
return nil
}
}
var baseURL: String {
return userDefaults?.string(forKey: urlKey) ?? "https://api.solian.app"
}
func makeRequest<T: Codable>(
path: String,
method: String = "GET",
headers: [String: String] = [:]
) async throws -> T? {
guard let token = token else {
throw RemoteError.missingCredentials
}
guard let url = URL(string: "\(baseURL)\(path)") else {
throw RemoteError.invalidURL
}
var request = URLRequest(url: url)
request.httpMethod = method
request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Accept")
for (key, value) in headers {
request.setValue(value, forHTTPHeaderField: key)
}
request.timeoutInterval = 10.0
print("[WidgetKit] [Network] Requesting: \(baseURL)\(path)")
let (data, response) = try await session.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw RemoteError.invalidResponse
}
print("[WidgetKit] [Network] Status: \(httpResponse.statusCode), Data length: \(data.count)")
if let jsonString = String(data: data, encoding: .utf8) {
print("[WidgetKit] [Network] Response: \(jsonString.prefix(500))")
}
switch httpResponse.statusCode {
case 200...299:
let decoder = JSONDecoder()
do {
let result = try decoder.decode(T.self, from: data)
print("[WidgetKit] [Network] Successfully decoded response")
return result
} catch {
print("[WidgetKit] [Network] Decoding error: \(error.localizedDescription)")
print("[WidgetKit] [Network] Expected type: \(String(describing: T.self))")
throw RemoteError.decodingError
}
case 404:
print("[WidgetKit] [Network] Resource not found (404)")
return nil
default:
print("[WidgetKit] [Network] HTTP Error: \(httpResponse.statusCode)")
throw RemoteError.httpError(httpResponse.statusCode)
}
}
}

View File

@@ -0,0 +1,46 @@
/* Check In Level Names */
"checkInResultT0" = "Great Misfortune";
"checkInResultT1" = "Misfortune";
"checkInResultT2" = "Moderate";
"checkInResultT3" = "Fortune";
"checkInResultT4" = "Great Fortune";
"checkInResultT5" = "Special";
/* Widget UI Strings */
"checkIn" = "Check In";
"tapToCheckIn" = "Tap to check in today";
"error" = "Error";
"openAppToRefresh" = "Open app to refresh";
"loading" = "Loading...";
"rewardPoints" = "%d";
"rewardExperience" = "%d EXP";
"footer" = "Solian Check In";
/* Notable Day Strings */
"notableDayToday" = "%@ is today!";
"notableDayIs" = "%@ is %@";
"notableDayUpcoming" = "Upcoming";
"today" = "Today";
/* Notification Widget Strings */
"notifications" = "Notifications";
"noNotifications" = "No notifications yet";
"tapToViewAll" = "Tap to view all notifications";
"justNow" = "Just now";
"minutesAgo" = "%d min ago";
"hoursAgo" = "%d hr ago";
"daysAgo" = "%d d ago";
"pleaseSignIn" = "Please open the app to sign in.";
"invalidServerConfig" = "Invalid server configuration.";
"invalidResponse" = "Server returned an invalid response.";
"httpError" = "Server error (%d).";
"decodingError" = "Failed to read server data.";
"unreadNotifications" = "Notification(s) unread";
"noNotifications" = "No notifications";
"noUnreadNotifications" = "All notifications are read";
"unread" = "unread";
/* Post Shuffle Widget Strings */
"noPostsAvailable" = "No posts available";
"tapToRefresh" = "Tap to refresh";
"loadingPost" = "Loading post...";

View File

@@ -0,0 +1,41 @@
/* Check In Level Names */
"checkInResultT0" = "Gran Desventura";
"checkInResultT1" = "Desventura";
"checkInResultT2" = "Moderado";
"checkInResultT3" = "Buena Fortuna";
"checkInResultT4" = "Gran Fortuna";
"checkInResultT5" = "Especial";
/* Widget UI Strings */
"checkIn" = "Registrar";
"tapToCheckIn" = "Toca para registrar hoy";
"error" = "Error";
"openAppToRefresh" = "Abre la aplicación para actualizar";
"loading" = "Cargando...";
"rewardPoints" = "%d";
"rewardExperience" = "%d EXP";
"footer" = "Registro Solian";
/* Notable Day Strings */
"notableDayToday" = "%@ es hoy!";
"notableDayIs" = "%@ es %@";
"today" = "Hoy";
/* Notification Widget Strings */
"notifications" = "Notificaciones";
"noNotifications" = "Aún no hay notificaciones";
"tapToViewAll" = "Toca para ver todas las notificaciones";
"justNow" = "Ahora mismo";
"minutesAgo" = "hace %d min";
"hoursAgo" = "hace %d hr";
"daysAgo" = "hace %d días";
"pleaseSignIn" = "Por favor, abre la aplicación para iniciar sesión.";
"invalidServerConfig" = "Configuración del servidor no válida.";
"invalidResponse" = "El servidor devolvió una respuesta no válida.";
"httpError" = "Error del servidor (%d).";
"decodingError" = "Error al leer los datos del servidor.";
/* Post Shuffle Widget Strings */
"noPostsAvailable" = "No hay publicaciones disponibles";
"tapToRefresh" = "Toca para actualizar";
"loadingPost" = "Cargando publicación...";

View File

@@ -0,0 +1,41 @@
/* Check In Level Names */
"checkInResultT0" = "大凶";
"checkInResultT1" = "凶";
"checkInResultT2" = "中平";
"checkInResultT3" = "吉";
"checkInResultT4" = "大吉";
"checkInResultT5" = "特殊";
/* Widget UI Strings */
"checkIn" = "チェックイン";
"tapToCheckIn" = "タップして今日チェックイン";
"error" = "エラー";
"openAppToRefresh" = "アプリを開いて更新";
"loading" = "読み込み中...";
"rewardPoints" = "%d";
"rewardExperience" = "%d 経験値";
"footer" = "Solian チェックイン";
/* Notable Day Strings */
"notableDayToday" = "%@は今日です!";
"notableDayIs" = "%@は%@です";
"today" = "今日";
/* Notification Widget Strings */
"notifications" = "通知";
"noNotifications" = "まだ通知はありません";
"tapToViewAll" = "タップしてすべての通知を表示";
"justNow" = "たった今";
"minutesAgo" = "%d分前";
"hoursAgo" = "%d時間前";
"daysAgo" = "%d日前";
"pleaseSignIn" = "アプリを開いてサインインしてください。";
"invalidServerConfig" = "サーバー設定が無効です。";
"invalidResponse" = "サーバーから無効な応答が返されました。";
"httpError" = "サーバーエラー (%d)。";
"decodingError" = "サーバーデータの読み込みに失敗しました。";
/* Post Shuffle Widget Strings */
"noPostsAvailable" = "利用可能な投稿がありません";
"tapToRefresh" = "タップして更新";
"loadingPost" = "投稿を読み込み中...";

View File

@@ -0,0 +1,41 @@
/* Check In Level Names */
"checkInResultT0" = "대흉";
"checkInResultT1" = "흉";
"checkInResultT2" = "중평";
"checkInResultT3" = "길";
"checkInResultT4" = "대길";
"checkInResultT5" = "특수";
/* Widget UI Strings */
"checkIn" = "체크인";
"tapToCheckIn" = "탭하여 오늘 체크인";
"error" = "오류";
"openAppToRefresh" = "앱을 열어 새로고침";
"loading" = "로딩 중...";
"rewardPoints" = "%d";
"rewardExperience" = "%d 경험치";
"footer" = "Solian 체크인";
/* Notable Day Strings */
"notableDayToday" = "%@ 오늘입니다!";
"notableDayIs" = "%@ 은/는 %@";
"today" = "오늘";
/* Notification Widget Strings */
"notifications" = "알림";
"noNotifications" = "아직 알림이 없습니다";
"tapToViewAll" = "탭하여 모든 알림 보기";
"justNow" = "방금";
"minutesAgo" = "%d분 전";
"hoursAgo" = "%d시간 전";
"daysAgo" = "%d일 전";
"pleaseSignIn" = "앱을 열어 로그인하세요.";
"invalidServerConfig" = "서버 구성이 올바르지 않습니다.";
"invalidResponse" = "서버에서 잘못된 응답을 받았습니다.";
"httpError" = "서버 오류 (%d).";
"decodingError" = "서버 데이터 읽기에 실패했습니다.";
/* Post Shuffle Widget Strings */
"noPostsAvailable" = "사용 가능한 게시물이 없습니다";
"tapToRefresh" = "탭하여 새로고침";
"loadingPost" = "게시물 로딩 중...";

View File

@@ -0,0 +1,46 @@
/* Check In Level Names */
"checkInResultT0" = "大凶";
"checkInResultT1" = "凶";
"checkInResultT2" = "中平";
"checkInResultT3" = "吉";
"checkInResultT4" = "大吉";
"checkInResultT5" = "特殊";
/* Widget UI Strings */
"checkIn" = "打卡";
"tapToCheckIn" = "点击今日打卡";
"error" = "错误";
"openAppToRefresh" = "打开应用以刷新";
"loading" = "加载中...";
"rewardPoints" = "%d";
"rewardExperience" = "%d 经验值";
"footer" = "Solian 签到";
/* Notable Day Strings */
"notableDayToday" = "%@是今天!";
"notableDayIs" = "%@ 是 %@";
"notableDayUpcoming" = "接下来";
"today" = "今天";
/* Notification Widget Strings */
"notifications" = "通知";
"noNotifications" = "还没有通知";
"tapToViewAll" = "点击查看所有通知";
"justNow" = "刚刚";
"minutesAgo" = "%d分钟前";
"hoursAgo" = "%d小时前";
"daysAgo" = "%d天前";
"pleaseSignIn" = "请打开应用登录。";
"invalidServerConfig" = "服务器配置无效。";
"invalidResponse" = "服务器返回了无效响应。";
"httpError" = "服务器错误 (%d)。";
"decodingError" = "读取服务器数据失败。";
"unreadNotifications" = "条通知未读";
"noNotifications" = "没有通知";
"noUnreadNotifications" = "没有未读通知";
"unread" = "未读";
/* Post Shuffle Widget Strings */
"noPostsAvailable" = "没有可用的帖子";
"tapToRefresh" = "点击刷新";
"loadingPost" = "加载帖子中...";

View File

@@ -0,0 +1,41 @@
/* Check In Level Names */
"checkInResultT0" = "大凶";
"checkInResultT1" = "凶";
"checkInResultT2" = "中平";
"checkInResultT3" = "吉";
"checkInResultT4" = "大吉";
"checkInResultT5" = "特殊";
/* Widget UI Strings */
"checkIn" = "打卡";
"tapToCheckIn" = "點擊今日打卡";
"error" = "錯誤";
"openAppToRefresh" = "打開應用以刷新";
"loading" = "載入中...";
"rewardPoints" = "%d";
"rewardExperience" = "%d 經驗值";
"footer" = "Solian 簽到";
/* Notable Day Strings */
"notableDayToday" = "%@是今天!";
"notableDayIs" = "%@ 是 %@";
"today" = "今天";
/* Notification Widget Strings */
"notifications" = "通知";
"noNotifications" = "還沒有通知";
"tapToViewAll" = "點擊查看所有通知";
"justNow" = "剛剛";
"minutesAgo" = "%d分鐘前";
"hoursAgo" = "%d小時前";
"daysAgo" = "%d天前";
"pleaseSignIn" = "請打開應用登錄。";
"invalidServerConfig" = "伺服器配置無效。";
"invalidResponse" = "伺服器返回了無效響應。";
"httpError" = "伺服器錯誤 (%d)。";
"decodingError" = "讀取伺服器數據失敗。";
/* Post Shuffle Widget Strings */
"noPostsAvailable" = "沒有可用的帖子";
"tapToRefresh" = "點擊刷新";
"loadingPost" = "載入帖子中...";

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.solsynth.solian</string>
</array>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>

View File

@@ -1,5 +1,6 @@
import 'dart:io';
import 'package:croppy/croppy.dart';
import 'package:dio/dio.dart';
import 'package:easy_localization/easy_localization.dart' hide TextDirection;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
@@ -20,7 +21,10 @@ import 'package:island/pods/userinfo.dart';
import 'package:island/pods/websocket.dart';
import 'package:island/route.dart';
import 'package:island/services/notify.dart';
import 'package:island/services/widget_sync_service.dart';
import 'package:island/services/timezone.dart';
import 'package:island/services/app_intents.dart';
import 'package:island/services/quick_actions.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:relative_time/relative_time.dart';
@@ -97,6 +101,28 @@ void main() async {
talker.error("[SplashScreen] Failed to load timezone database... $err");
}
try {
talker.info("[AppIntents] Initializing App Intents service...");
final appIntentsService = AppIntentsService();
await appIntentsService.initialize();
talker.info("[AppIntents] App Intents service is ready!");
} catch (err) {
talker.error(
"[AppIntents] Failed to initialize App Intents service... $err",
);
}
try {
talker.info("[QuickActions] Initializing Quick Actions service...");
final quickActionsService = QuickActionsService();
await quickActionsService.initialize();
talker.info("[QuickActions] Quick Actions service is ready!");
} catch (err) {
talker.error(
"[QuickActions] Failed to initialize Quick Actions service... $err",
);
}
final prefs = await SharedPreferences.getInstance();
if (!kIsWeb && (Platform.isMacOS || Platform.isLinux || Platform.isWindows)) {
@@ -169,6 +195,12 @@ void main() async {
runApp(
ProviderScope(
retry: (retryCount, error) {
if (error is DioException && error.response?.statusCode == 404) {
return null;
}
return const Duration(milliseconds: 300);
},
observers: [
TalkerRiverpodObserver(
talker: talker,
@@ -282,6 +314,11 @@ class IslandApp extends HookConsumerWidget {
ref.listen(websocketStateProvider, (_, state) {
talker.info('[WebSocket] $state');
});
ref.listen(userInfoProvider, (_, user) {
if (user.value != null) {
WidgetSyncService().syncToWidget();
}
});
Future(() {
userNotifier.fetchUser().then((_) {
final user = ref.watch(userInfoProvider);

108
lib/models/activitypub.dart Normal file
View File

@@ -0,0 +1,108 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'activitypub.freezed.dart';
part 'activitypub.g.dart';
@freezed
sealed class SnActivityPubInstance with _$SnActivityPubInstance {
const factory SnActivityPubInstance({
required String id,
required String domain,
String? name,
String? description,
String? software,
String? version,
String? iconUrl,
String? thumbnailUrl,
String? contactEmail,
String? contactAccountUsername,
int? activeUsers,
@Default(false) bool isBlocked,
@Default(false) bool isSilenced,
String? blockReason,
Map<String, dynamic>? metadata,
DateTime? lastFetchedAt,
DateTime? lastActivityAt,
DateTime? metadataFetchedAt,
}) = _SnActivityPubInstance;
factory SnActivityPubInstance.fromJson(Map<String, dynamic> json) =>
_$SnActivityPubInstanceFromJson(json);
}
@freezed
sealed class SnActivityPubUser with _$SnActivityPubUser {
const factory SnActivityPubUser({
required String actorUri,
required String username,
required String displayName,
required String bio,
required String avatarUrl,
required DateTime followedAt,
required bool isLocal,
required String instanceDomain,
}) = _SnActivityPubUser;
factory SnActivityPubUser.fromJson(Map<String, dynamic> json) =>
_$SnActivityPubUserFromJson(json);
}
@freezed
sealed class SnActivityPubActor with _$SnActivityPubActor {
const factory SnActivityPubActor({
required String id,
required String uri,
@Default('') String type,
String? displayName,
String? username,
String? summary,
String? inboxUri,
String? outboxUri,
String? followersUri,
String? followingUri,
String? featuredUri,
String? avatarUrl,
String? headerUrl,
String? publicKeyId,
String? publicKey,
@Default(false) bool isBot,
@Default(false) bool isLocked,
@Default(true) bool discoverable,
@Default(false) bool manuallyApprovesFollowers,
Map<String, dynamic>? endpoints,
Map<String, dynamic>? publicKeyData,
Map<String, dynamic>? metadata,
DateTime? lastFetchedAt,
DateTime? lastActivityAt,
required SnActivityPubInstance instance,
required String instanceId,
bool? isFollowing,
}) = _SnActivityPubActor;
factory SnActivityPubActor.fromJson(Map<String, dynamic> json) =>
_$SnActivityPubActorFromJson(json);
}
@freezed
sealed class SnActivityPubFollowResponse with _$SnActivityPubFollowResponse {
const factory SnActivityPubFollowResponse({
required bool success,
required String message,
}) = _SnActivityPubFollowResponse;
factory SnActivityPubFollowResponse.fromJson(Map<String, dynamic> json) =>
_$SnActivityPubFollowResponseFromJson(json);
}
@freezed
sealed class SnActorStatusResponse with _$SnActorStatusResponse {
const factory SnActorStatusResponse({
required bool enabled,
@Default(0) int followerCount,
SnActivityPubActor? actor,
String? actorUri,
}) = _SnActorStatusResponse;
factory SnActorStatusResponse.fromJson(Map<String, dynamic> json) =>
_$SnActorStatusResponseFromJson(json);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,186 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'activitypub.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_SnActivityPubInstance _$SnActivityPubInstanceFromJson(
Map<String, dynamic> json,
) => _SnActivityPubInstance(
id: json['id'] as String,
domain: json['domain'] as String,
name: json['name'] as String?,
description: json['description'] as String?,
software: json['software'] as String?,
version: json['version'] as String?,
iconUrl: json['icon_url'] as String?,
thumbnailUrl: json['thumbnail_url'] as String?,
contactEmail: json['contact_email'] as String?,
contactAccountUsername: json['contact_account_username'] as String?,
activeUsers: (json['active_users'] as num?)?.toInt(),
isBlocked: json['is_blocked'] as bool? ?? false,
isSilenced: json['is_silenced'] as bool? ?? false,
blockReason: json['block_reason'] as String?,
metadata: json['metadata'] as Map<String, dynamic>?,
lastFetchedAt: json['last_fetched_at'] == null
? null
: DateTime.parse(json['last_fetched_at'] as String),
lastActivityAt: json['last_activity_at'] == null
? null
: DateTime.parse(json['last_activity_at'] as String),
metadataFetchedAt: json['metadata_fetched_at'] == null
? null
: DateTime.parse(json['metadata_fetched_at'] as String),
);
Map<String, dynamic> _$SnActivityPubInstanceToJson(
_SnActivityPubInstance instance,
) => <String, dynamic>{
'id': instance.id,
'domain': instance.domain,
'name': instance.name,
'description': instance.description,
'software': instance.software,
'version': instance.version,
'icon_url': instance.iconUrl,
'thumbnail_url': instance.thumbnailUrl,
'contact_email': instance.contactEmail,
'contact_account_username': instance.contactAccountUsername,
'active_users': instance.activeUsers,
'is_blocked': instance.isBlocked,
'is_silenced': instance.isSilenced,
'block_reason': instance.blockReason,
'metadata': instance.metadata,
'last_fetched_at': instance.lastFetchedAt?.toIso8601String(),
'last_activity_at': instance.lastActivityAt?.toIso8601String(),
'metadata_fetched_at': instance.metadataFetchedAt?.toIso8601String(),
};
_SnActivityPubUser _$SnActivityPubUserFromJson(Map<String, dynamic> json) =>
_SnActivityPubUser(
actorUri: json['actor_uri'] as String,
username: json['username'] as String,
displayName: json['display_name'] as String,
bio: json['bio'] as String,
avatarUrl: json['avatar_url'] as String,
followedAt: DateTime.parse(json['followed_at'] as String),
isLocal: json['is_local'] as bool,
instanceDomain: json['instance_domain'] as String,
);
Map<String, dynamic> _$SnActivityPubUserToJson(_SnActivityPubUser instance) =>
<String, dynamic>{
'actor_uri': instance.actorUri,
'username': instance.username,
'display_name': instance.displayName,
'bio': instance.bio,
'avatar_url': instance.avatarUrl,
'followed_at': instance.followedAt.toIso8601String(),
'is_local': instance.isLocal,
'instance_domain': instance.instanceDomain,
};
_SnActivityPubActor _$SnActivityPubActorFromJson(Map<String, dynamic> json) =>
_SnActivityPubActor(
id: json['id'] as String,
uri: json['uri'] as String,
type: json['type'] as String? ?? '',
displayName: json['display_name'] as String?,
username: json['username'] as String?,
summary: json['summary'] as String?,
inboxUri: json['inbox_uri'] as String?,
outboxUri: json['outbox_uri'] as String?,
followersUri: json['followers_uri'] as String?,
followingUri: json['following_uri'] as String?,
featuredUri: json['featured_uri'] as String?,
avatarUrl: json['avatar_url'] as String?,
headerUrl: json['header_url'] as String?,
publicKeyId: json['public_key_id'] as String?,
publicKey: json['public_key'] as String?,
isBot: json['is_bot'] as bool? ?? false,
isLocked: json['is_locked'] as bool? ?? false,
discoverable: json['discoverable'] as bool? ?? true,
manuallyApprovesFollowers:
json['manually_approves_followers'] as bool? ?? false,
endpoints: json['endpoints'] as Map<String, dynamic>?,
publicKeyData: json['public_key_data'] as Map<String, dynamic>?,
metadata: json['metadata'] as Map<String, dynamic>?,
lastFetchedAt: json['last_fetched_at'] == null
? null
: DateTime.parse(json['last_fetched_at'] as String),
lastActivityAt: json['last_activity_at'] == null
? null
: DateTime.parse(json['last_activity_at'] as String),
instance: SnActivityPubInstance.fromJson(
json['instance'] as Map<String, dynamic>,
),
instanceId: json['instance_id'] as String,
isFollowing: json['is_following'] as bool?,
);
Map<String, dynamic> _$SnActivityPubActorToJson(_SnActivityPubActor instance) =>
<String, dynamic>{
'id': instance.id,
'uri': instance.uri,
'type': instance.type,
'display_name': instance.displayName,
'username': instance.username,
'summary': instance.summary,
'inbox_uri': instance.inboxUri,
'outbox_uri': instance.outboxUri,
'followers_uri': instance.followersUri,
'following_uri': instance.followingUri,
'featured_uri': instance.featuredUri,
'avatar_url': instance.avatarUrl,
'header_url': instance.headerUrl,
'public_key_id': instance.publicKeyId,
'public_key': instance.publicKey,
'is_bot': instance.isBot,
'is_locked': instance.isLocked,
'discoverable': instance.discoverable,
'manually_approves_followers': instance.manuallyApprovesFollowers,
'endpoints': instance.endpoints,
'public_key_data': instance.publicKeyData,
'metadata': instance.metadata,
'last_fetched_at': instance.lastFetchedAt?.toIso8601String(),
'last_activity_at': instance.lastActivityAt?.toIso8601String(),
'instance': instance.instance.toJson(),
'instance_id': instance.instanceId,
'is_following': instance.isFollowing,
};
_SnActivityPubFollowResponse _$SnActivityPubFollowResponseFromJson(
Map<String, dynamic> json,
) => _SnActivityPubFollowResponse(
success: json['success'] as bool,
message: json['message'] as String,
);
Map<String, dynamic> _$SnActivityPubFollowResponseToJson(
_SnActivityPubFollowResponse instance,
) => <String, dynamic>{
'success': instance.success,
'message': instance.message,
};
_SnActorStatusResponse _$SnActorStatusResponseFromJson(
Map<String, dynamic> json,
) => _SnActorStatusResponse(
enabled: json['enabled'] as bool,
followerCount: (json['follower_count'] as num?)?.toInt() ?? 0,
actor: json['actor'] == null
? null
: SnActivityPubActor.fromJson(json['actor'] as Map<String, dynamic>),
actorUri: json['actor_uri'] as String?,
);
Map<String, dynamic> _$SnActorStatusResponseToJson(
_SnActorStatusResponse instance,
) => <String, dynamic>{
'enabled': instance.enabled,
'follower_count': instance.followerCount,
'actor': instance.actor?.toJson(),
'actor_uri': instance.actorUri,
};

View File

@@ -55,6 +55,7 @@ sealed class SnCloudFile with _$SnCloudFile {
required DateTime createdAt,
required DateTime updatedAt,
required DateTime? deletedAt,
String? url,
}) = _SnCloudFile;
factory SnCloudFile.fromJson(Map<String, dynamic> json) =>

View File

@@ -281,7 +281,7 @@ as String?,
/// @nodoc
mixin _$SnCloudFile {
String get id; String get name; String? get description; Map<String, dynamic>? get fileMeta; Map<String, dynamic>? get userMeta; SnFilePool? get pool; List<int> get sensitiveMarks; String? get mimeType; String? get hash; int get size; DateTime? get uploadedAt; String? get uploadedTo; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
String get id; String get name; String? get description; Map<String, dynamic>? get fileMeta; Map<String, dynamic>? get userMeta; SnFilePool? get pool; List<int> get sensitiveMarks; String? get mimeType; String? get hash; int get size; DateTime? get uploadedAt; String? get uploadedTo; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String? get url;
/// Create a copy of SnCloudFile
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -294,16 +294,16 @@ $SnCloudFileCopyWith<SnCloudFile> get copyWith => _$SnCloudFileCopyWithImpl<SnCl
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnCloudFile&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&const DeepCollectionEquality().equals(other.fileMeta, fileMeta)&&const DeepCollectionEquality().equals(other.userMeta, userMeta)&&(identical(other.pool, pool) || other.pool == pool)&&const DeepCollectionEquality().equals(other.sensitiveMarks, sensitiveMarks)&&(identical(other.mimeType, mimeType) || other.mimeType == mimeType)&&(identical(other.hash, hash) || other.hash == hash)&&(identical(other.size, size) || other.size == size)&&(identical(other.uploadedAt, uploadedAt) || other.uploadedAt == uploadedAt)&&(identical(other.uploadedTo, uploadedTo) || other.uploadedTo == uploadedTo)&&(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 SnCloudFile&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&const DeepCollectionEquality().equals(other.fileMeta, fileMeta)&&const DeepCollectionEquality().equals(other.userMeta, userMeta)&&(identical(other.pool, pool) || other.pool == pool)&&const DeepCollectionEquality().equals(other.sensitiveMarks, sensitiveMarks)&&(identical(other.mimeType, mimeType) || other.mimeType == mimeType)&&(identical(other.hash, hash) || other.hash == hash)&&(identical(other.size, size) || other.size == size)&&(identical(other.uploadedAt, uploadedAt) || other.uploadedAt == uploadedAt)&&(identical(other.uploadedTo, uploadedTo) || other.uploadedTo == uploadedTo)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.url, url) || other.url == url));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,name,description,const DeepCollectionEquality().hash(fileMeta),const DeepCollectionEquality().hash(userMeta),pool,const DeepCollectionEquality().hash(sensitiveMarks),mimeType,hash,size,uploadedAt,uploadedTo,createdAt,updatedAt,deletedAt);
int get hashCode => Object.hash(runtimeType,id,name,description,const DeepCollectionEquality().hash(fileMeta),const DeepCollectionEquality().hash(userMeta),pool,const DeepCollectionEquality().hash(sensitiveMarks),mimeType,hash,size,uploadedAt,uploadedTo,createdAt,updatedAt,deletedAt,url);
@override
String toString() {
return 'SnCloudFile(id: $id, name: $name, description: $description, fileMeta: $fileMeta, userMeta: $userMeta, pool: $pool, sensitiveMarks: $sensitiveMarks, mimeType: $mimeType, hash: $hash, size: $size, uploadedAt: $uploadedAt, uploadedTo: $uploadedTo, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
return 'SnCloudFile(id: $id, name: $name, description: $description, fileMeta: $fileMeta, userMeta: $userMeta, pool: $pool, sensitiveMarks: $sensitiveMarks, mimeType: $mimeType, hash: $hash, size: $size, uploadedAt: $uploadedAt, uploadedTo: $uploadedTo, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, url: $url)';
}
@@ -314,7 +314,7 @@ abstract mixin class $SnCloudFileCopyWith<$Res> {
factory $SnCloudFileCopyWith(SnCloudFile value, $Res Function(SnCloudFile) _then) = _$SnCloudFileCopyWithImpl;
@useResult
$Res call({
String id, String name, String? description, Map<String, dynamic>? fileMeta, Map<String, dynamic>? userMeta, SnFilePool? pool, List<int> sensitiveMarks, String? mimeType, String? hash, int size, DateTime? uploadedAt, String? uploadedTo, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
String id, String name, String? description, Map<String, dynamic>? fileMeta, Map<String, dynamic>? userMeta, SnFilePool? pool, List<int> sensitiveMarks, String? mimeType, String? hash, int size, DateTime? uploadedAt, String? uploadedTo, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String? url
});
@@ -331,7 +331,7 @@ class _$SnCloudFileCopyWithImpl<$Res>
/// Create a copy of SnCloudFile
/// 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? description = freezed,Object? fileMeta = freezed,Object? userMeta = freezed,Object? pool = freezed,Object? sensitiveMarks = null,Object? mimeType = freezed,Object? hash = freezed,Object? size = null,Object? uploadedAt = freezed,Object? uploadedTo = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? description = freezed,Object? fileMeta = freezed,Object? userMeta = freezed,Object? pool = freezed,Object? sensitiveMarks = null,Object? mimeType = freezed,Object? hash = freezed,Object? size = null,Object? uploadedAt = freezed,Object? uploadedTo = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? url = freezed,}) {
return _then(_self.copyWith(
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
@@ -348,7 +348,8 @@ as DateTime?,uploadedTo: freezed == uploadedTo ? _self.uploadedTo : uploadedTo /
as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
as DateTime?,url: freezed == url ? _self.url : url // ignore: cast_nullable_to_non_nullable
as String?,
));
}
/// Create a copy of SnCloudFile
@@ -442,10 +443,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String name, String? description, Map<String, dynamic>? fileMeta, Map<String, dynamic>? userMeta, SnFilePool? pool, List<int> sensitiveMarks, String? mimeType, String? hash, int size, DateTime? uploadedAt, String? uploadedTo, 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? description, Map<String, dynamic>? fileMeta, Map<String, dynamic>? userMeta, SnFilePool? pool, List<int> sensitiveMarks, String? mimeType, String? hash, int size, DateTime? uploadedAt, String? uploadedTo, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String? url)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SnCloudFile() when $default != null:
return $default(_that.id,_that.name,_that.description,_that.fileMeta,_that.userMeta,_that.pool,_that.sensitiveMarks,_that.mimeType,_that.hash,_that.size,_that.uploadedAt,_that.uploadedTo,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
return $default(_that.id,_that.name,_that.description,_that.fileMeta,_that.userMeta,_that.pool,_that.sensitiveMarks,_that.mimeType,_that.hash,_that.size,_that.uploadedAt,_that.uploadedTo,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.url);case _:
return orElse();
}
@@ -463,10 +464,10 @@ return $default(_that.id,_that.name,_that.description,_that.fileMeta,_that.userM
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String name, String? description, Map<String, dynamic>? fileMeta, Map<String, dynamic>? userMeta, SnFilePool? pool, List<int> sensitiveMarks, String? mimeType, String? hash, int size, DateTime? uploadedAt, String? uploadedTo, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String name, String? description, Map<String, dynamic>? fileMeta, Map<String, dynamic>? userMeta, SnFilePool? pool, List<int> sensitiveMarks, String? mimeType, String? hash, int size, DateTime? uploadedAt, String? uploadedTo, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String? url) $default,) {final _that = this;
switch (_that) {
case _SnCloudFile():
return $default(_that.id,_that.name,_that.description,_that.fileMeta,_that.userMeta,_that.pool,_that.sensitiveMarks,_that.mimeType,_that.hash,_that.size,_that.uploadedAt,_that.uploadedTo,_that.createdAt,_that.updatedAt,_that.deletedAt);}
return $default(_that.id,_that.name,_that.description,_that.fileMeta,_that.userMeta,_that.pool,_that.sensitiveMarks,_that.mimeType,_that.hash,_that.size,_that.uploadedAt,_that.uploadedTo,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.url);}
}
/// A variant of `when` that fallback to returning `null`
///
@@ -480,10 +481,10 @@ return $default(_that.id,_that.name,_that.description,_that.fileMeta,_that.userM
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String name, String? description, Map<String, dynamic>? fileMeta, Map<String, dynamic>? userMeta, SnFilePool? pool, List<int> sensitiveMarks, String? mimeType, String? hash, int size, DateTime? uploadedAt, String? uploadedTo, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String name, String? description, Map<String, dynamic>? fileMeta, Map<String, dynamic>? userMeta, SnFilePool? pool, List<int> sensitiveMarks, String? mimeType, String? hash, int size, DateTime? uploadedAt, String? uploadedTo, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String? url)? $default,) {final _that = this;
switch (_that) {
case _SnCloudFile() when $default != null:
return $default(_that.id,_that.name,_that.description,_that.fileMeta,_that.userMeta,_that.pool,_that.sensitiveMarks,_that.mimeType,_that.hash,_that.size,_that.uploadedAt,_that.uploadedTo,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
return $default(_that.id,_that.name,_that.description,_that.fileMeta,_that.userMeta,_that.pool,_that.sensitiveMarks,_that.mimeType,_that.hash,_that.size,_that.uploadedAt,_that.uploadedTo,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.url);case _:
return null;
}
@@ -495,7 +496,7 @@ return $default(_that.id,_that.name,_that.description,_that.fileMeta,_that.userM
@JsonSerializable()
class _SnCloudFile implements SnCloudFile {
const _SnCloudFile({required this.id, required this.name, required this.description, required final Map<String, dynamic>? fileMeta, required final Map<String, dynamic>? userMeta, required this.pool, final List<int> sensitiveMarks = const [], required this.mimeType, required this.hash, required this.size, required this.uploadedAt, required this.uploadedTo, required this.createdAt, required this.updatedAt, required this.deletedAt}): _fileMeta = fileMeta,_userMeta = userMeta,_sensitiveMarks = sensitiveMarks;
const _SnCloudFile({required this.id, required this.name, required this.description, required final Map<String, dynamic>? fileMeta, required final Map<String, dynamic>? userMeta, required this.pool, final List<int> sensitiveMarks = const [], required this.mimeType, required this.hash, required this.size, required this.uploadedAt, required this.uploadedTo, required this.createdAt, required this.updatedAt, required this.deletedAt, this.url}): _fileMeta = fileMeta,_userMeta = userMeta,_sensitiveMarks = sensitiveMarks;
factory _SnCloudFile.fromJson(Map<String, dynamic> json) => _$SnCloudFileFromJson(json);
@override final String id;
@@ -535,6 +536,7 @@ class _SnCloudFile implements SnCloudFile {
@override final DateTime createdAt;
@override final DateTime updatedAt;
@override final DateTime? deletedAt;
@override final String? url;
/// Create a copy of SnCloudFile
/// with the given fields replaced by the non-null parameter values.
@@ -549,16 +551,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnCloudFile&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&const DeepCollectionEquality().equals(other._fileMeta, _fileMeta)&&const DeepCollectionEquality().equals(other._userMeta, _userMeta)&&(identical(other.pool, pool) || other.pool == pool)&&const DeepCollectionEquality().equals(other._sensitiveMarks, _sensitiveMarks)&&(identical(other.mimeType, mimeType) || other.mimeType == mimeType)&&(identical(other.hash, hash) || other.hash == hash)&&(identical(other.size, size) || other.size == size)&&(identical(other.uploadedAt, uploadedAt) || other.uploadedAt == uploadedAt)&&(identical(other.uploadedTo, uploadedTo) || other.uploadedTo == uploadedTo)&&(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 _SnCloudFile&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&const DeepCollectionEquality().equals(other._fileMeta, _fileMeta)&&const DeepCollectionEquality().equals(other._userMeta, _userMeta)&&(identical(other.pool, pool) || other.pool == pool)&&const DeepCollectionEquality().equals(other._sensitiveMarks, _sensitiveMarks)&&(identical(other.mimeType, mimeType) || other.mimeType == mimeType)&&(identical(other.hash, hash) || other.hash == hash)&&(identical(other.size, size) || other.size == size)&&(identical(other.uploadedAt, uploadedAt) || other.uploadedAt == uploadedAt)&&(identical(other.uploadedTo, uploadedTo) || other.uploadedTo == uploadedTo)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.url, url) || other.url == url));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,name,description,const DeepCollectionEquality().hash(_fileMeta),const DeepCollectionEquality().hash(_userMeta),pool,const DeepCollectionEquality().hash(_sensitiveMarks),mimeType,hash,size,uploadedAt,uploadedTo,createdAt,updatedAt,deletedAt);
int get hashCode => Object.hash(runtimeType,id,name,description,const DeepCollectionEquality().hash(_fileMeta),const DeepCollectionEquality().hash(_userMeta),pool,const DeepCollectionEquality().hash(_sensitiveMarks),mimeType,hash,size,uploadedAt,uploadedTo,createdAt,updatedAt,deletedAt,url);
@override
String toString() {
return 'SnCloudFile(id: $id, name: $name, description: $description, fileMeta: $fileMeta, userMeta: $userMeta, pool: $pool, sensitiveMarks: $sensitiveMarks, mimeType: $mimeType, hash: $hash, size: $size, uploadedAt: $uploadedAt, uploadedTo: $uploadedTo, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
return 'SnCloudFile(id: $id, name: $name, description: $description, fileMeta: $fileMeta, userMeta: $userMeta, pool: $pool, sensitiveMarks: $sensitiveMarks, mimeType: $mimeType, hash: $hash, size: $size, uploadedAt: $uploadedAt, uploadedTo: $uploadedTo, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, url: $url)';
}
@@ -569,7 +571,7 @@ abstract mixin class _$SnCloudFileCopyWith<$Res> implements $SnCloudFileCopyWith
factory _$SnCloudFileCopyWith(_SnCloudFile value, $Res Function(_SnCloudFile) _then) = __$SnCloudFileCopyWithImpl;
@override @useResult
$Res call({
String id, String name, String? description, Map<String, dynamic>? fileMeta, Map<String, dynamic>? userMeta, SnFilePool? pool, List<int> sensitiveMarks, String? mimeType, String? hash, int size, DateTime? uploadedAt, String? uploadedTo, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
String id, String name, String? description, Map<String, dynamic>? fileMeta, Map<String, dynamic>? userMeta, SnFilePool? pool, List<int> sensitiveMarks, String? mimeType, String? hash, int size, DateTime? uploadedAt, String? uploadedTo, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String? url
});
@@ -586,7 +588,7 @@ class __$SnCloudFileCopyWithImpl<$Res>
/// Create a copy of SnCloudFile
/// 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? description = freezed,Object? fileMeta = freezed,Object? userMeta = freezed,Object? pool = freezed,Object? sensitiveMarks = null,Object? mimeType = freezed,Object? hash = freezed,Object? size = null,Object? uploadedAt = freezed,Object? uploadedTo = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? description = freezed,Object? fileMeta = freezed,Object? userMeta = freezed,Object? pool = freezed,Object? sensitiveMarks = null,Object? mimeType = freezed,Object? hash = freezed,Object? size = null,Object? uploadedAt = freezed,Object? uploadedTo = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? url = freezed,}) {
return _then(_SnCloudFile(
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
@@ -603,7 +605,8 @@ as DateTime?,uploadedTo: freezed == uploadedTo ? _self.uploadedTo : uploadedTo /
as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
as DateTime?,url: freezed == url ? _self.url : url // ignore: cast_nullable_to_non_nullable
as String?,
));
}

View File

@@ -55,6 +55,7 @@ _SnCloudFile _$SnCloudFileFromJson(Map<String, dynamic> json) => _SnCloudFile(
deletedAt: json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
url: json['url'] as String?,
);
Map<String, dynamic> _$SnCloudFileToJson(_SnCloudFile instance) =>
@@ -74,6 +75,7 @@ Map<String, dynamic> _$SnCloudFileToJson(_SnCloudFile instance) =>
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
'url': instance.url,
};
_SnCloudFileIndex _$SnCloudFileIndexFromJson(Map<String, dynamic> json) =>

View File

@@ -1,5 +1,6 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:island/models/account.dart';
import 'package:island/models/activitypub.dart';
import 'package:island/models/file.dart';
import 'package:island/models/post_category.dart';
import 'package:island/models/post_tag.dart';
@@ -39,8 +40,14 @@ sealed class SnPost with _$SnPost {
SnPost? forwardedPost,
String? realmId,
SnRealm? realm,
String? publisherId,
SnPublisher? publisher,
String? actorid,
SnActivityPubActor? actor,
String? fediverseUri,
int? fediverseType,
@Default(0) int contentType,
@Default([]) List<SnCloudFile> attachments,
required SnPublisher publisher,
@Default({}) Map<String, int> reactionsCount,
@Default({}) Map<String, bool> reactionsMade,
@Default([]) List<dynamic> reactions,
@@ -155,10 +162,12 @@ sealed class SnPostReaction with _$SnPostReaction {
required String symbol,
required int attitude,
required String postId,
required String accountId,
required DateTime createdAt,
required DateTime updatedAt,
@Default(null) SnAccount? account,
String? actorId,
SnActivityPubActor? actor,
String? accountId,
SnAccount? account,
DateTime? deletedAt,
}) = _SnPostReaction;

View File

@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$SnPost {
String get id; String? get title; String? get description; String? get language; DateTime? get editedAt; DateTime? get publishedAt; int get visibility; String? get content; String? get slug; int get type; Map<String, dynamic>? get meta; SnPostEmbedView? get embedView; int get viewsUnique; int get viewsTotal; int get upvotes; int get downvotes; int get repliesCount; int get awardedScore; int? get pinMode; String? get threadedPostId; SnPost? get threadedPost; String? get repliedPostId; SnPost? get repliedPost; String? get forwardedPostId; SnPost? get forwardedPost; String? get realmId; SnRealm? get realm; List<SnCloudFile> get attachments; SnPublisher get publisher; Map<String, int> get reactionsCount; Map<String, bool> get reactionsMade; List<dynamic> get reactions; List<SnPostTag> get tags; List<SnPostCategory> get categories; List<dynamic> get collections; List<SnPostFeaturedRecord> get featuredRecords; DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; bool get repliedGone; bool get forwardedGone; bool get isTruncated;
String get id; String? get title; String? get description; String? get language; DateTime? get editedAt; DateTime? get publishedAt; int get visibility; String? get content; String? get slug; int get type; Map<String, dynamic>? get meta; SnPostEmbedView? get embedView; int get viewsUnique; int get viewsTotal; int get upvotes; int get downvotes; int get repliesCount; int get awardedScore; int? get pinMode; String? get threadedPostId; SnPost? get threadedPost; String? get repliedPostId; SnPost? get repliedPost; String? get forwardedPostId; SnPost? get forwardedPost; String? get realmId; SnRealm? get realm; String? get publisherId; SnPublisher? get publisher; String? get actorid; SnActivityPubActor? get actor; String? get fediverseUri; int? get fediverseType; int get contentType; List<SnCloudFile> get attachments; 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; List<SnPostFeaturedRecord> get featuredRecords; DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; bool get repliedGone; bool get forwardedGone; bool get isTruncated;
/// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -28,16 +28,16 @@ $SnPostCopyWith<SnPost> get copyWith => _$SnPostCopyWithImpl<SnPost>(this as SnP
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.meta, meta)&&(identical(other.embedView, embedView) || other.embedView == embedView)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.awardedScore, awardedScore) || other.awardedScore == awardedScore)&&(identical(other.pinMode, pinMode) || other.pinMode == pinMode)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&const DeepCollectionEquality().equals(other.attachments, attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other.reactionsCount, reactionsCount)&&const DeepCollectionEquality().equals(other.reactionsMade, reactionsMade)&&const DeepCollectionEquality().equals(other.reactions, reactions)&&const DeepCollectionEquality().equals(other.tags, tags)&&const DeepCollectionEquality().equals(other.categories, categories)&&const DeepCollectionEquality().equals(other.collections, collections)&&const DeepCollectionEquality().equals(other.featuredRecords, featuredRecords)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.repliedGone, repliedGone) || other.repliedGone == repliedGone)&&(identical(other.forwardedGone, forwardedGone) || other.forwardedGone == forwardedGone)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated));
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.meta, meta)&&(identical(other.embedView, embedView) || other.embedView == embedView)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.awardedScore, awardedScore) || other.awardedScore == awardedScore)&&(identical(other.pinMode, pinMode) || other.pinMode == pinMode)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&(identical(other.actorid, actorid) || other.actorid == actorid)&&(identical(other.actor, actor) || other.actor == actor)&&(identical(other.fediverseUri, fediverseUri) || other.fediverseUri == fediverseUri)&&(identical(other.fediverseType, fediverseType) || other.fediverseType == fediverseType)&&(identical(other.contentType, contentType) || other.contentType == contentType)&&const DeepCollectionEquality().equals(other.attachments, attachments)&&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)&&const DeepCollectionEquality().equals(other.featuredRecords, featuredRecords)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.repliedGone, repliedGone) || other.repliedGone == repliedGone)&&(identical(other.forwardedGone, forwardedGone) || other.forwardedGone == forwardedGone)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,slug,type,const DeepCollectionEquality().hash(meta),embedView,viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,awardedScore,pinMode,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,realmId,realm,const DeepCollectionEquality().hash(attachments),publisher,const DeepCollectionEquality().hash(reactionsCount),const DeepCollectionEquality().hash(reactionsMade),const DeepCollectionEquality().hash(reactions),const DeepCollectionEquality().hash(tags),const DeepCollectionEquality().hash(categories),const DeepCollectionEquality().hash(collections),const DeepCollectionEquality().hash(featuredRecords),createdAt,updatedAt,deletedAt,repliedGone,forwardedGone,isTruncated]);
int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,slug,type,const DeepCollectionEquality().hash(meta),embedView,viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,awardedScore,pinMode,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,realmId,realm,publisherId,publisher,actorid,actor,fediverseUri,fediverseType,contentType,const DeepCollectionEquality().hash(attachments),const DeepCollectionEquality().hash(reactionsCount),const DeepCollectionEquality().hash(reactionsMade),const DeepCollectionEquality().hash(reactions),const DeepCollectionEquality().hash(tags),const DeepCollectionEquality().hash(categories),const DeepCollectionEquality().hash(collections),const DeepCollectionEquality().hash(featuredRecords),createdAt,updatedAt,deletedAt,repliedGone,forwardedGone,isTruncated]);
@override
String toString() {
return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, slug: $slug, type: $type, meta: $meta, embedView: $embedView, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, awardedScore: $awardedScore, pinMode: $pinMode, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, realmId: $realmId, realm: $realm, attachments: $attachments, publisher: $publisher, reactionsCount: $reactionsCount, reactionsMade: $reactionsMade, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, featuredRecords: $featuredRecords, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, repliedGone: $repliedGone, forwardedGone: $forwardedGone, isTruncated: $isTruncated)';
return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, slug: $slug, type: $type, meta: $meta, embedView: $embedView, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, awardedScore: $awardedScore, pinMode: $pinMode, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, realmId: $realmId, realm: $realm, publisherId: $publisherId, publisher: $publisher, actorid: $actorid, actor: $actor, fediverseUri: $fediverseUri, fediverseType: $fediverseType, contentType: $contentType, attachments: $attachments, reactionsCount: $reactionsCount, reactionsMade: $reactionsMade, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, featuredRecords: $featuredRecords, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, repliedGone: $repliedGone, forwardedGone: $forwardedGone, isTruncated: $isTruncated)';
}
@@ -48,11 +48,11 @@ abstract mixin class $SnPostCopyWith<$Res> {
factory $SnPostCopyWith(SnPost value, $Res Function(SnPost) _then) = _$SnPostCopyWithImpl;
@useResult
$Res call({
String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, SnPostEmbedView? embedView, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int awardedScore, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, List<SnPostFeaturedRecord> featuredRecords, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool repliedGone, bool forwardedGone, bool isTruncated
String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, SnPostEmbedView? embedView, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int awardedScore, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, String? publisherId, SnPublisher? publisher, String? actorid, SnActivityPubActor? actor, String? fediverseUri, int? fediverseType, int contentType, List<SnCloudFile> attachments, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, List<SnPostFeaturedRecord> featuredRecords, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool repliedGone, bool forwardedGone, bool isTruncated
});
$SnPostEmbedViewCopyWith<$Res>? get embedView;$SnPostCopyWith<$Res>? get threadedPost;$SnPostCopyWith<$Res>? get repliedPost;$SnPostCopyWith<$Res>? get forwardedPost;$SnRealmCopyWith<$Res>? get realm;$SnPublisherCopyWith<$Res> get publisher;
$SnPostEmbedViewCopyWith<$Res>? get embedView;$SnPostCopyWith<$Res>? get threadedPost;$SnPostCopyWith<$Res>? get repliedPost;$SnPostCopyWith<$Res>? get forwardedPost;$SnRealmCopyWith<$Res>? get realm;$SnPublisherCopyWith<$Res>? get publisher;$SnActivityPubActorCopyWith<$Res>? get actor;
}
/// @nodoc
@@ -65,7 +65,7 @@ class _$SnPostCopyWithImpl<$Res>
/// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? slug = freezed,Object? type = null,Object? meta = freezed,Object? embedView = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? awardedScore = null,Object? pinMode = freezed,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? realmId = freezed,Object? realm = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactionsMade = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? featuredRecords = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? repliedGone = null,Object? forwardedGone = null,Object? isTruncated = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? slug = freezed,Object? type = null,Object? meta = freezed,Object? embedView = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? awardedScore = null,Object? pinMode = freezed,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? realmId = freezed,Object? realm = freezed,Object? publisherId = freezed,Object? publisher = freezed,Object? actorid = freezed,Object? actor = freezed,Object? fediverseUri = freezed,Object? fediverseType = freezed,Object? contentType = null,Object? attachments = null,Object? reactionsCount = null,Object? reactionsMade = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? featuredRecords = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? repliedGone = null,Object? forwardedGone = null,Object? isTruncated = null,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
@@ -94,9 +94,15 @@ as SnPost?,forwardedPostId: freezed == forwardedPostId ? _self.forwardedPostId :
as String?,forwardedPost: freezed == forwardedPost ? _self.forwardedPost : forwardedPost // 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 SnPublisher,reactionsCount: null == reactionsCount ? _self.reactionsCount : reactionsCount // ignore: cast_nullable_to_non_nullable
as SnRealm?,publisherId: freezed == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable
as String?,publisher: freezed == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable
as SnPublisher?,actorid: freezed == actorid ? _self.actorid : actorid // ignore: cast_nullable_to_non_nullable
as String?,actor: freezed == actor ? _self.actor : actor // ignore: cast_nullable_to_non_nullable
as SnActivityPubActor?,fediverseUri: freezed == fediverseUri ? _self.fediverseUri : fediverseUri // ignore: cast_nullable_to_non_nullable
as String?,fediverseType: freezed == fediverseType ? _self.fediverseType : fediverseType // ignore: cast_nullable_to_non_nullable
as int?,contentType: null == contentType ? _self.contentType : contentType // ignore: cast_nullable_to_non_nullable
as int,attachments: null == attachments ? _self.attachments : attachments // ignore: cast_nullable_to_non_nullable
as List<SnCloudFile>,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, bool>,reactions: null == reactions ? _self.reactions : reactions // ignore: cast_nullable_to_non_nullable
as List<dynamic>,tags: null == tags ? _self.tags : tags // ignore: cast_nullable_to_non_nullable
@@ -176,11 +182,26 @@ $SnRealmCopyWith<$Res>? get realm {
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnPublisherCopyWith<$Res> get publisher {
return $SnPublisherCopyWith<$Res>(_self.publisher, (value) {
$SnPublisherCopyWith<$Res>? get publisher {
if (_self.publisher == null) {
return null;
}
return $SnPublisherCopyWith<$Res>(_self.publisher!, (value) {
return _then(_self.copyWith(publisher: value));
});
}/// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnActivityPubActorCopyWith<$Res>? get actor {
if (_self.actor == null) {
return null;
}
return $SnActivityPubActorCopyWith<$Res>(_self.actor!, (value) {
return _then(_self.copyWith(actor: value));
});
}
}
@@ -260,10 +281,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, SnPostEmbedView? embedView, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int awardedScore, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, List<SnPostFeaturedRecord> featuredRecords, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool repliedGone, bool forwardedGone, bool isTruncated)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, SnPostEmbedView? embedView, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int awardedScore, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, String? publisherId, SnPublisher? publisher, String? actorid, SnActivityPubActor? actor, String? fediverseUri, int? fediverseType, int contentType, List<SnCloudFile> attachments, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, List<SnPostFeaturedRecord> featuredRecords, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool repliedGone, bool forwardedGone, bool isTruncated)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SnPost() when $default != null:
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.embedView,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.awardedScore,_that.pinMode,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.featuredRecords,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.repliedGone,_that.forwardedGone,_that.isTruncated);case _:
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.embedView,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.awardedScore,_that.pinMode,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.publisherId,_that.publisher,_that.actorid,_that.actor,_that.fediverseUri,_that.fediverseType,_that.contentType,_that.attachments,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.featuredRecords,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.repliedGone,_that.forwardedGone,_that.isTruncated);case _:
return orElse();
}
@@ -281,10 +302,10 @@ return $default(_that.id,_that.title,_that.description,_that.language,_that.edit
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, SnPostEmbedView? embedView, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int awardedScore, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, List<SnPostFeaturedRecord> featuredRecords, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool repliedGone, bool forwardedGone, bool isTruncated) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, SnPostEmbedView? embedView, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int awardedScore, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, String? publisherId, SnPublisher? publisher, String? actorid, SnActivityPubActor? actor, String? fediverseUri, int? fediverseType, int contentType, List<SnCloudFile> attachments, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, List<SnPostFeaturedRecord> featuredRecords, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool repliedGone, bool forwardedGone, bool isTruncated) $default,) {final _that = this;
switch (_that) {
case _SnPost():
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.embedView,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.awardedScore,_that.pinMode,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.featuredRecords,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.repliedGone,_that.forwardedGone,_that.isTruncated);}
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.embedView,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.awardedScore,_that.pinMode,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.publisherId,_that.publisher,_that.actorid,_that.actor,_that.fediverseUri,_that.fediverseType,_that.contentType,_that.attachments,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.featuredRecords,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.repliedGone,_that.forwardedGone,_that.isTruncated);}
}
/// A variant of `when` that fallback to returning `null`
///
@@ -298,10 +319,10 @@ return $default(_that.id,_that.title,_that.description,_that.language,_that.edit
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, SnPostEmbedView? embedView, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int awardedScore, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, List<SnPostFeaturedRecord> featuredRecords, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool repliedGone, bool forwardedGone, bool isTruncated)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, SnPostEmbedView? embedView, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int awardedScore, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, String? publisherId, SnPublisher? publisher, String? actorid, SnActivityPubActor? actor, String? fediverseUri, int? fediverseType, int contentType, List<SnCloudFile> attachments, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, List<SnPostFeaturedRecord> featuredRecords, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool repliedGone, bool forwardedGone, bool isTruncated)? $default,) {final _that = this;
switch (_that) {
case _SnPost() when $default != null:
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.embedView,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.awardedScore,_that.pinMode,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.featuredRecords,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.repliedGone,_that.forwardedGone,_that.isTruncated);case _:
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.slug,_that.type,_that.meta,_that.embedView,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.awardedScore,_that.pinMode,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.realmId,_that.realm,_that.publisherId,_that.publisher,_that.actorid,_that.actor,_that.fediverseUri,_that.fediverseType,_that.contentType,_that.attachments,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.featuredRecords,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.repliedGone,_that.forwardedGone,_that.isTruncated);case _:
return null;
}
@@ -313,7 +334,7 @@ return $default(_that.id,_that.title,_that.description,_that.language,_that.edit
@JsonSerializable()
class _SnPost implements SnPost {
const _SnPost({required this.id, this.title, this.description, this.language, this.editedAt, this.publishedAt = null, this.visibility = 0, this.content, this.slug, this.type = 0, final Map<String, dynamic>? meta, this.embedView, this.viewsUnique = 0, this.viewsTotal = 0, this.upvotes = 0, this.downvotes = 0, this.repliesCount = 0, this.awardedScore = 0, this.pinMode, this.threadedPostId, this.threadedPost, this.repliedPostId, this.repliedPost, this.forwardedPostId, this.forwardedPost, this.realmId, this.realm, final List<SnCloudFile> attachments = const [], required this.publisher, final Map<String, int> reactionsCount = const {}, final Map<String, bool> reactionsMade = const {}, final List<dynamic> reactions = const [], final List<SnPostTag> tags = const [], final List<SnPostCategory> categories = const [], final List<dynamic> collections = const [], final List<SnPostFeaturedRecord> featuredRecords = const [], this.createdAt = null, this.updatedAt = null, this.deletedAt, this.repliedGone = false, this.forwardedGone = false, this.isTruncated = false}): _meta = meta,_attachments = attachments,_reactionsCount = reactionsCount,_reactionsMade = reactionsMade,_reactions = reactions,_tags = tags,_categories = categories,_collections = collections,_featuredRecords = featuredRecords;
const _SnPost({required this.id, this.title, this.description, this.language, this.editedAt, this.publishedAt = null, this.visibility = 0, this.content, this.slug, this.type = 0, final Map<String, dynamic>? meta, this.embedView, this.viewsUnique = 0, this.viewsTotal = 0, this.upvotes = 0, this.downvotes = 0, this.repliesCount = 0, this.awardedScore = 0, this.pinMode, this.threadedPostId, this.threadedPost, this.repliedPostId, this.repliedPost, this.forwardedPostId, this.forwardedPost, this.realmId, this.realm, this.publisherId, this.publisher, this.actorid, this.actor, this.fediverseUri, this.fediverseType, this.contentType = 0, final List<SnCloudFile> attachments = const [], 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 [], final List<SnPostFeaturedRecord> featuredRecords = const [], this.createdAt = null, this.updatedAt = null, this.deletedAt, this.repliedGone = false, this.forwardedGone = false, this.isTruncated = false}): _meta = meta,_attachments = attachments,_reactionsCount = reactionsCount,_reactionsMade = reactionsMade,_reactions = reactions,_tags = tags,_categories = categories,_collections = collections,_featuredRecords = featuredRecords;
factory _SnPost.fromJson(Map<String, dynamic> json) => _$SnPostFromJson(json);
@override final String id;
@@ -351,6 +372,13 @@ class _SnPost implements SnPost {
@override final SnPost? forwardedPost;
@override final String? realmId;
@override final SnRealm? realm;
@override final String? publisherId;
@override final SnPublisher? publisher;
@override final String? actorid;
@override final SnActivityPubActor? actor;
@override final String? fediverseUri;
@override final int? fediverseType;
@override@JsonKey() final int contentType;
final List<SnCloudFile> _attachments;
@override@JsonKey() List<SnCloudFile> get attachments {
if (_attachments is EqualUnmodifiableListView) return _attachments;
@@ -358,7 +386,6 @@ class _SnPost implements SnPost {
return EqualUnmodifiableListView(_attachments);
}
@override final SnPublisher publisher;
final Map<String, int> _reactionsCount;
@override@JsonKey() Map<String, int> get reactionsCount {
if (_reactionsCount is EqualUnmodifiableMapView) return _reactionsCount;
@@ -428,16 +455,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._meta, _meta)&&(identical(other.embedView, embedView) || other.embedView == embedView)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.awardedScore, awardedScore) || other.awardedScore == awardedScore)&&(identical(other.pinMode, pinMode) || other.pinMode == pinMode)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&const DeepCollectionEquality().equals(other._attachments, _attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other._reactionsCount, _reactionsCount)&&const DeepCollectionEquality().equals(other._reactionsMade, _reactionsMade)&&const DeepCollectionEquality().equals(other._reactions, _reactions)&&const DeepCollectionEquality().equals(other._tags, _tags)&&const DeepCollectionEquality().equals(other._categories, _categories)&&const DeepCollectionEquality().equals(other._collections, _collections)&&const DeepCollectionEquality().equals(other._featuredRecords, _featuredRecords)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.repliedGone, repliedGone) || other.repliedGone == repliedGone)&&(identical(other.forwardedGone, forwardedGone) || other.forwardedGone == forwardedGone)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._meta, _meta)&&(identical(other.embedView, embedView) || other.embedView == embedView)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.awardedScore, awardedScore) || other.awardedScore == awardedScore)&&(identical(other.pinMode, pinMode) || other.pinMode == pinMode)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.realm, realm) || other.realm == realm)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&(identical(other.actorid, actorid) || other.actorid == actorid)&&(identical(other.actor, actor) || other.actor == actor)&&(identical(other.fediverseUri, fediverseUri) || other.fediverseUri == fediverseUri)&&(identical(other.fediverseType, fediverseType) || other.fediverseType == fediverseType)&&(identical(other.contentType, contentType) || other.contentType == contentType)&&const DeepCollectionEquality().equals(other._attachments, _attachments)&&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)&&const DeepCollectionEquality().equals(other._featuredRecords, _featuredRecords)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.repliedGone, repliedGone) || other.repliedGone == repliedGone)&&(identical(other.forwardedGone, forwardedGone) || other.forwardedGone == forwardedGone)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,slug,type,const DeepCollectionEquality().hash(_meta),embedView,viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,awardedScore,pinMode,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,realmId,realm,const DeepCollectionEquality().hash(_attachments),publisher,const DeepCollectionEquality().hash(_reactionsCount),const DeepCollectionEquality().hash(_reactionsMade),const DeepCollectionEquality().hash(_reactions),const DeepCollectionEquality().hash(_tags),const DeepCollectionEquality().hash(_categories),const DeepCollectionEquality().hash(_collections),const DeepCollectionEquality().hash(_featuredRecords),createdAt,updatedAt,deletedAt,repliedGone,forwardedGone,isTruncated]);
int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,slug,type,const DeepCollectionEquality().hash(_meta),embedView,viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,awardedScore,pinMode,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,realmId,realm,publisherId,publisher,actorid,actor,fediverseUri,fediverseType,contentType,const DeepCollectionEquality().hash(_attachments),const DeepCollectionEquality().hash(_reactionsCount),const DeepCollectionEquality().hash(_reactionsMade),const DeepCollectionEquality().hash(_reactions),const DeepCollectionEquality().hash(_tags),const DeepCollectionEquality().hash(_categories),const DeepCollectionEquality().hash(_collections),const DeepCollectionEquality().hash(_featuredRecords),createdAt,updatedAt,deletedAt,repliedGone,forwardedGone,isTruncated]);
@override
String toString() {
return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, slug: $slug, type: $type, meta: $meta, embedView: $embedView, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, awardedScore: $awardedScore, pinMode: $pinMode, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, realmId: $realmId, realm: $realm, attachments: $attachments, publisher: $publisher, reactionsCount: $reactionsCount, reactionsMade: $reactionsMade, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, featuredRecords: $featuredRecords, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, repliedGone: $repliedGone, forwardedGone: $forwardedGone, isTruncated: $isTruncated)';
return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, slug: $slug, type: $type, meta: $meta, embedView: $embedView, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, awardedScore: $awardedScore, pinMode: $pinMode, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, realmId: $realmId, realm: $realm, publisherId: $publisherId, publisher: $publisher, actorid: $actorid, actor: $actor, fediverseUri: $fediverseUri, fediverseType: $fediverseType, contentType: $contentType, attachments: $attachments, reactionsCount: $reactionsCount, reactionsMade: $reactionsMade, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, featuredRecords: $featuredRecords, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, repliedGone: $repliedGone, forwardedGone: $forwardedGone, isTruncated: $isTruncated)';
}
@@ -448,11 +475,11 @@ abstract mixin class _$SnPostCopyWith<$Res> implements $SnPostCopyWith<$Res> {
factory _$SnPostCopyWith(_SnPost value, $Res Function(_SnPost) _then) = __$SnPostCopyWithImpl;
@override @useResult
$Res call({
String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, SnPostEmbedView? embedView, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int awardedScore, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, List<SnPostFeaturedRecord> featuredRecords, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool repliedGone, bool forwardedGone, bool isTruncated
String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, String? slug, int type, Map<String, dynamic>? meta, SnPostEmbedView? embedView, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, int awardedScore, int? pinMode, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, String? realmId, SnRealm? realm, String? publisherId, SnPublisher? publisher, String? actorid, SnActivityPubActor? actor, String? fediverseUri, int? fediverseType, int contentType, List<SnCloudFile> attachments, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, List<SnPostFeaturedRecord> featuredRecords, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool repliedGone, bool forwardedGone, bool isTruncated
});
@override $SnPostEmbedViewCopyWith<$Res>? get embedView;@override $SnPostCopyWith<$Res>? get threadedPost;@override $SnPostCopyWith<$Res>? get repliedPost;@override $SnPostCopyWith<$Res>? get forwardedPost;@override $SnRealmCopyWith<$Res>? get realm;@override $SnPublisherCopyWith<$Res> get publisher;
@override $SnPostEmbedViewCopyWith<$Res>? get embedView;@override $SnPostCopyWith<$Res>? get threadedPost;@override $SnPostCopyWith<$Res>? get repliedPost;@override $SnPostCopyWith<$Res>? get forwardedPost;@override $SnRealmCopyWith<$Res>? get realm;@override $SnPublisherCopyWith<$Res>? get publisher;@override $SnActivityPubActorCopyWith<$Res>? get actor;
}
/// @nodoc
@@ -465,7 +492,7 @@ class __$SnPostCopyWithImpl<$Res>
/// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? slug = freezed,Object? type = null,Object? meta = freezed,Object? embedView = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? awardedScore = null,Object? pinMode = freezed,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? realmId = freezed,Object? realm = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactionsMade = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? featuredRecords = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? repliedGone = null,Object? forwardedGone = null,Object? isTruncated = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? slug = freezed,Object? type = null,Object? meta = freezed,Object? embedView = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? awardedScore = null,Object? pinMode = freezed,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? realmId = freezed,Object? realm = freezed,Object? publisherId = freezed,Object? publisher = freezed,Object? actorid = freezed,Object? actor = freezed,Object? fediverseUri = freezed,Object? fediverseType = freezed,Object? contentType = null,Object? attachments = null,Object? reactionsCount = null,Object? reactionsMade = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? featuredRecords = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? repliedGone = null,Object? forwardedGone = null,Object? isTruncated = null,}) {
return _then(_SnPost(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
@@ -494,9 +521,15 @@ as SnPost?,forwardedPostId: freezed == forwardedPostId ? _self.forwardedPostId :
as String?,forwardedPost: freezed == forwardedPost ? _self.forwardedPost : forwardedPost // 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 SnPublisher,reactionsCount: null == reactionsCount ? _self._reactionsCount : reactionsCount // ignore: cast_nullable_to_non_nullable
as SnRealm?,publisherId: freezed == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable
as String?,publisher: freezed == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable
as SnPublisher?,actorid: freezed == actorid ? _self.actorid : actorid // ignore: cast_nullable_to_non_nullable
as String?,actor: freezed == actor ? _self.actor : actor // ignore: cast_nullable_to_non_nullable
as SnActivityPubActor?,fediverseUri: freezed == fediverseUri ? _self.fediverseUri : fediverseUri // ignore: cast_nullable_to_non_nullable
as String?,fediverseType: freezed == fediverseType ? _self.fediverseType : fediverseType // ignore: cast_nullable_to_non_nullable
as int?,contentType: null == contentType ? _self.contentType : contentType // ignore: cast_nullable_to_non_nullable
as int,attachments: null == attachments ? _self._attachments : attachments // ignore: cast_nullable_to_non_nullable
as List<SnCloudFile>,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, bool>,reactions: null == reactions ? _self._reactions : reactions // ignore: cast_nullable_to_non_nullable
as List<dynamic>,tags: null == tags ? _self._tags : tags // ignore: cast_nullable_to_non_nullable
@@ -577,11 +610,26 @@ $SnRealmCopyWith<$Res>? get realm {
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnPublisherCopyWith<$Res> get publisher {
return $SnPublisherCopyWith<$Res>(_self.publisher, (value) {
$SnPublisherCopyWith<$Res>? get publisher {
if (_self.publisher == null) {
return null;
}
return $SnPublisherCopyWith<$Res>(_self.publisher!, (value) {
return _then(_self.copyWith(publisher: value));
});
}/// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnActivityPubActorCopyWith<$Res>? get actor {
if (_self.actor == null) {
return null;
}
return $SnActivityPubActorCopyWith<$Res>(_self.actor!, (value) {
return _then(_self.copyWith(actor: value));
});
}
}
@@ -1937,7 +1985,7 @@ as DateTime?,
/// @nodoc
mixin _$SnPostReaction {
String get id; String get symbol; int get attitude; String get postId; String get accountId; DateTime get createdAt; DateTime get updatedAt; SnAccount? get account; DateTime? get deletedAt;
String get id; String get symbol; int get attitude; String get postId; DateTime get createdAt; DateTime get updatedAt; String? get actorId; SnActivityPubActor? get actor; String? get accountId; SnAccount? get account; DateTime? get deletedAt;
/// Create a copy of SnPostReaction
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -1950,16 +1998,16 @@ $SnPostReactionCopyWith<SnPostReaction> get copyWith => _$SnPostReactionCopyWith
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPostReaction&&(identical(other.id, id) || other.id == id)&&(identical(other.symbol, symbol) || other.symbol == symbol)&&(identical(other.attitude, attitude) || other.attitude == attitude)&&(identical(other.postId, postId) || other.postId == postId)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.account, account) || other.account == account)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPostReaction&&(identical(other.id, id) || other.id == id)&&(identical(other.symbol, symbol) || other.symbol == symbol)&&(identical(other.attitude, attitude) || other.attitude == attitude)&&(identical(other.postId, postId) || other.postId == postId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.actorId, actorId) || other.actorId == actorId)&&(identical(other.actor, actor) || other.actor == actor)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,symbol,attitude,postId,accountId,createdAt,updatedAt,account,deletedAt);
int get hashCode => Object.hash(runtimeType,id,symbol,attitude,postId,createdAt,updatedAt,actorId,actor,accountId,account,deletedAt);
@override
String toString() {
return 'SnPostReaction(id: $id, symbol: $symbol, attitude: $attitude, postId: $postId, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, account: $account, deletedAt: $deletedAt)';
return 'SnPostReaction(id: $id, symbol: $symbol, attitude: $attitude, postId: $postId, createdAt: $createdAt, updatedAt: $updatedAt, actorId: $actorId, actor: $actor, accountId: $accountId, account: $account, deletedAt: $deletedAt)';
}
@@ -1970,11 +2018,11 @@ abstract mixin class $SnPostReactionCopyWith<$Res> {
factory $SnPostReactionCopyWith(SnPostReaction value, $Res Function(SnPostReaction) _then) = _$SnPostReactionCopyWithImpl;
@useResult
$Res call({
String id, String symbol, int attitude, String postId, String accountId, DateTime createdAt, DateTime updatedAt, SnAccount? account, DateTime? deletedAt
String id, String symbol, int attitude, String postId, DateTime createdAt, DateTime updatedAt, String? actorId, SnActivityPubActor? actor, String? accountId, SnAccount? account, DateTime? deletedAt
});
$SnAccountCopyWith<$Res>? get account;
$SnActivityPubActorCopyWith<$Res>? get actor;$SnAccountCopyWith<$Res>? get account;
}
/// @nodoc
@@ -1987,16 +2035,18 @@ class _$SnPostReactionCopyWithImpl<$Res>
/// Create a copy of SnPostReaction
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? symbol = null,Object? attitude = null,Object? postId = null,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? account = freezed,Object? deletedAt = freezed,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? symbol = null,Object? attitude = null,Object? postId = null,Object? createdAt = null,Object? updatedAt = null,Object? actorId = freezed,Object? actor = freezed,Object? accountId = freezed,Object? account = freezed,Object? deletedAt = freezed,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,symbol: null == symbol ? _self.symbol : symbol // ignore: cast_nullable_to_non_nullable
as String,attitude: null == attitude ? _self.attitude : attitude // ignore: cast_nullable_to_non_nullable
as int,postId: null == postId ? _self.postId : postId // ignore: cast_nullable_to_non_nullable
as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
as DateTime,actorId: freezed == actorId ? _self.actorId : actorId // ignore: cast_nullable_to_non_nullable
as String?,actor: freezed == actor ? _self.actor : actor // ignore: cast_nullable_to_non_nullable
as SnActivityPubActor?,accountId: freezed == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
as String?,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
as SnAccount?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
));
@@ -2005,6 +2055,18 @@ as DateTime?,
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnActivityPubActorCopyWith<$Res>? get actor {
if (_self.actor == null) {
return null;
}
return $SnActivityPubActorCopyWith<$Res>(_self.actor!, (value) {
return _then(_self.copyWith(actor: value));
});
}/// Create a copy of SnPostReaction
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAccountCopyWith<$Res>? get account {
if (_self.account == null) {
return null;
@@ -2092,10 +2154,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String symbol, int attitude, String postId, String accountId, DateTime createdAt, DateTime updatedAt, SnAccount? account, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String symbol, int attitude, String postId, DateTime createdAt, DateTime updatedAt, String? actorId, SnActivityPubActor? actor, String? accountId, SnAccount? account, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SnPostReaction() when $default != null:
return $default(_that.id,_that.symbol,_that.attitude,_that.postId,_that.accountId,_that.createdAt,_that.updatedAt,_that.account,_that.deletedAt);case _:
return $default(_that.id,_that.symbol,_that.attitude,_that.postId,_that.createdAt,_that.updatedAt,_that.actorId,_that.actor,_that.accountId,_that.account,_that.deletedAt);case _:
return orElse();
}
@@ -2113,10 +2175,10 @@ return $default(_that.id,_that.symbol,_that.attitude,_that.postId,_that.accountI
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String symbol, int attitude, String postId, String accountId, DateTime createdAt, DateTime updatedAt, SnAccount? account, DateTime? deletedAt) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String symbol, int attitude, String postId, DateTime createdAt, DateTime updatedAt, String? actorId, SnActivityPubActor? actor, String? accountId, SnAccount? account, DateTime? deletedAt) $default,) {final _that = this;
switch (_that) {
case _SnPostReaction():
return $default(_that.id,_that.symbol,_that.attitude,_that.postId,_that.accountId,_that.createdAt,_that.updatedAt,_that.account,_that.deletedAt);}
return $default(_that.id,_that.symbol,_that.attitude,_that.postId,_that.createdAt,_that.updatedAt,_that.actorId,_that.actor,_that.accountId,_that.account,_that.deletedAt);}
}
/// A variant of `when` that fallback to returning `null`
///
@@ -2130,10 +2192,10 @@ return $default(_that.id,_that.symbol,_that.attitude,_that.postId,_that.accountI
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String symbol, int attitude, String postId, String accountId, DateTime createdAt, DateTime updatedAt, SnAccount? account, DateTime? deletedAt)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String symbol, int attitude, String postId, DateTime createdAt, DateTime updatedAt, String? actorId, SnActivityPubActor? actor, String? accountId, SnAccount? account, DateTime? deletedAt)? $default,) {final _that = this;
switch (_that) {
case _SnPostReaction() when $default != null:
return $default(_that.id,_that.symbol,_that.attitude,_that.postId,_that.accountId,_that.createdAt,_that.updatedAt,_that.account,_that.deletedAt);case _:
return $default(_that.id,_that.symbol,_that.attitude,_that.postId,_that.createdAt,_that.updatedAt,_that.actorId,_that.actor,_that.accountId,_that.account,_that.deletedAt);case _:
return null;
}
@@ -2145,17 +2207,19 @@ return $default(_that.id,_that.symbol,_that.attitude,_that.postId,_that.accountI
@JsonSerializable()
class _SnPostReaction implements SnPostReaction {
const _SnPostReaction({required this.id, required this.symbol, required this.attitude, required this.postId, required this.accountId, required this.createdAt, required this.updatedAt, this.account = null, this.deletedAt});
const _SnPostReaction({required this.id, required this.symbol, required this.attitude, required this.postId, required this.createdAt, required this.updatedAt, this.actorId, this.actor, this.accountId, this.account, this.deletedAt});
factory _SnPostReaction.fromJson(Map<String, dynamic> json) => _$SnPostReactionFromJson(json);
@override final String id;
@override final String symbol;
@override final int attitude;
@override final String postId;
@override final String accountId;
@override final DateTime createdAt;
@override final DateTime updatedAt;
@override@JsonKey() final SnAccount? account;
@override final String? actorId;
@override final SnActivityPubActor? actor;
@override final String? accountId;
@override final SnAccount? account;
@override final DateTime? deletedAt;
/// Create a copy of SnPostReaction
@@ -2171,16 +2235,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPostReaction&&(identical(other.id, id) || other.id == id)&&(identical(other.symbol, symbol) || other.symbol == symbol)&&(identical(other.attitude, attitude) || other.attitude == attitude)&&(identical(other.postId, postId) || other.postId == postId)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.account, account) || other.account == account)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPostReaction&&(identical(other.id, id) || other.id == id)&&(identical(other.symbol, symbol) || other.symbol == symbol)&&(identical(other.attitude, attitude) || other.attitude == attitude)&&(identical(other.postId, postId) || other.postId == postId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.actorId, actorId) || other.actorId == actorId)&&(identical(other.actor, actor) || other.actor == actor)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,symbol,attitude,postId,accountId,createdAt,updatedAt,account,deletedAt);
int get hashCode => Object.hash(runtimeType,id,symbol,attitude,postId,createdAt,updatedAt,actorId,actor,accountId,account,deletedAt);
@override
String toString() {
return 'SnPostReaction(id: $id, symbol: $symbol, attitude: $attitude, postId: $postId, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, account: $account, deletedAt: $deletedAt)';
return 'SnPostReaction(id: $id, symbol: $symbol, attitude: $attitude, postId: $postId, createdAt: $createdAt, updatedAt: $updatedAt, actorId: $actorId, actor: $actor, accountId: $accountId, account: $account, deletedAt: $deletedAt)';
}
@@ -2191,11 +2255,11 @@ abstract mixin class _$SnPostReactionCopyWith<$Res> implements $SnPostReactionCo
factory _$SnPostReactionCopyWith(_SnPostReaction value, $Res Function(_SnPostReaction) _then) = __$SnPostReactionCopyWithImpl;
@override @useResult
$Res call({
String id, String symbol, int attitude, String postId, String accountId, DateTime createdAt, DateTime updatedAt, SnAccount? account, DateTime? deletedAt
String id, String symbol, int attitude, String postId, DateTime createdAt, DateTime updatedAt, String? actorId, SnActivityPubActor? actor, String? accountId, SnAccount? account, DateTime? deletedAt
});
@override $SnAccountCopyWith<$Res>? get account;
@override $SnActivityPubActorCopyWith<$Res>? get actor;@override $SnAccountCopyWith<$Res>? get account;
}
/// @nodoc
@@ -2208,16 +2272,18 @@ class __$SnPostReactionCopyWithImpl<$Res>
/// Create a copy of SnPostReaction
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? symbol = null,Object? attitude = null,Object? postId = null,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? account = freezed,Object? deletedAt = freezed,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? symbol = null,Object? attitude = null,Object? postId = null,Object? createdAt = null,Object? updatedAt = null,Object? actorId = freezed,Object? actor = freezed,Object? accountId = freezed,Object? account = freezed,Object? deletedAt = freezed,}) {
return _then(_SnPostReaction(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,symbol: null == symbol ? _self.symbol : symbol // ignore: cast_nullable_to_non_nullable
as String,attitude: null == attitude ? _self.attitude : attitude // ignore: cast_nullable_to_non_nullable
as int,postId: null == postId ? _self.postId : postId // ignore: cast_nullable_to_non_nullable
as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
as DateTime,actorId: freezed == actorId ? _self.actorId : actorId // ignore: cast_nullable_to_non_nullable
as String?,actor: freezed == actor ? _self.actor : actor // ignore: cast_nullable_to_non_nullable
as SnActivityPubActor?,accountId: freezed == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
as String?,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
as SnAccount?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
));
@@ -2227,6 +2293,18 @@ as DateTime?,
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnActivityPubActorCopyWith<$Res>? get actor {
if (_self.actor == null) {
return null;
}
return $SnActivityPubActorCopyWith<$Res>(_self.actor!, (value) {
return _then(_self.copyWith(actor: value));
});
}/// Create a copy of SnPostReaction
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAccountCopyWith<$Res>? get account {
if (_self.account == null) {
return null;

View File

@@ -48,12 +48,22 @@ _SnPost _$SnPostFromJson(Map<String, dynamic> json) => _SnPost(
realm: json['realm'] == null
? null
: SnRealm.fromJson(json['realm'] as Map<String, dynamic>),
publisherId: json['publisher_id'] as String?,
publisher: json['publisher'] == null
? null
: SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
actorid: json['actorid'] as String?,
actor: json['actor'] == null
? null
: SnActivityPubActor.fromJson(json['actor'] as Map<String, dynamic>),
fediverseUri: json['fediverse_uri'] as String?,
fediverseType: (json['fediverse_type'] as num?)?.toInt(),
contentType: (json['content_type'] as num?)?.toInt() ?? 0,
attachments:
(json['attachments'] as List<dynamic>?)
?.map((e) => SnCloudFile.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
publisher: SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
reactionsCount:
(json['reactions_count'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, (e as num).toInt()),
@@ -123,8 +133,14 @@ Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
'forwarded_post': instance.forwardedPost?.toJson(),
'realm_id': instance.realmId,
'realm': instance.realm?.toJson(),
'publisher_id': instance.publisherId,
'publisher': instance.publisher?.toJson(),
'actorid': instance.actorid,
'actor': instance.actor?.toJson(),
'fediverse_uri': instance.fediverseUri,
'fediverse_type': instance.fediverseType,
'content_type': instance.contentType,
'attachments': instance.attachments.map((e) => e.toJson()).toList(),
'publisher': instance.publisher.toJson(),
'reactions_count': instance.reactionsCount,
'reactions_made': instance.reactionsMade,
'reactions': instance.reactions,
@@ -232,9 +248,13 @@ _SnPostReaction _$SnPostReactionFromJson(Map<String, dynamic> json) =>
symbol: json['symbol'] as String,
attitude: (json['attitude'] as num).toInt(),
postId: json['post_id'] as String,
accountId: json['account_id'] as String,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
actorId: json['actor_id'] as String?,
actor: json['actor'] == null
? null
: SnActivityPubActor.fromJson(json['actor'] as Map<String, dynamic>),
accountId: json['account_id'] as String?,
account: json['account'] == null
? null
: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
@@ -249,9 +269,11 @@ Map<String, dynamic> _$SnPostReactionToJson(_SnPostReaction instance) =>
'symbol': instance.symbol,
'attitude': instance.attitude,
'post_id': instance.postId,
'account_id': instance.accountId,
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'actor_id': instance.actorId,
'actor': instance.actor?.toJson(),
'account_id': instance.accountId,
'account': instance.account?.toJson(),
'deleted_at': instance.deletedAt?.toIso8601String(),
};

View File

@@ -12,9 +12,10 @@ sealed class SnRelationship with _$SnRelationship {
required DateTime? updatedAt,
required DateTime? deletedAt,
required String accountId,
required SnAccount account,
// Usually the account was not included in the response
required SnAccount? account,
required String relatedId,
required SnAccount related,
required SnAccount? related,
required DateTime? expiredAt,
required int status,
}) = _SnRelationship;

View File

@@ -15,7 +15,8 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$SnRelationship {
DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; String get accountId; SnAccount get account; String get relatedId; SnAccount get related; DateTime? get expiredAt; int get status;
DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; String get accountId;// Usually the account was not included in the response
SnAccount? get account; String get relatedId; SnAccount? get related; DateTime? get expiredAt; int get status;
/// Create a copy of SnRelationship
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -48,11 +49,11 @@ abstract mixin class $SnRelationshipCopyWith<$Res> {
factory $SnRelationshipCopyWith(SnRelationship value, $Res Function(SnRelationship) _then) = _$SnRelationshipCopyWithImpl;
@useResult
$Res call({
DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount account, String relatedId, SnAccount related, DateTime? expiredAt, int status
DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount? account, String relatedId, SnAccount? related, DateTime? expiredAt, int status
});
$SnAccountCopyWith<$Res> get account;$SnAccountCopyWith<$Res> get related;
$SnAccountCopyWith<$Res>? get account;$SnAccountCopyWith<$Res>? get related;
}
/// @nodoc
@@ -65,16 +66,16 @@ class _$SnRelationshipCopyWithImpl<$Res>
/// Create a copy of SnRelationship
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? accountId = null,Object? account = null,Object? relatedId = null,Object? related = null,Object? expiredAt = freezed,Object? status = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? accountId = null,Object? account = freezed,Object? relatedId = null,Object? related = freezed,Object? expiredAt = freezed,Object? status = null,}) {
return _then(_self.copyWith(
createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
as String,account: null == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
as SnAccount,relatedId: null == relatedId ? _self.relatedId : relatedId // ignore: cast_nullable_to_non_nullable
as String,related: null == related ? _self.related : related // ignore: cast_nullable_to_non_nullable
as SnAccount,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
as String,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
as SnAccount?,relatedId: null == relatedId ? _self.relatedId : relatedId // ignore: cast_nullable_to_non_nullable
as String,related: freezed == related ? _self.related : related // ignore: cast_nullable_to_non_nullable
as SnAccount?,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
as DateTime?,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as int,
));
@@ -83,18 +84,24 @@ as int,
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAccountCopyWith<$Res> get account {
return $SnAccountCopyWith<$Res>(_self.account, (value) {
$SnAccountCopyWith<$Res>? get account {
if (_self.account == null) {
return null;
}
return $SnAccountCopyWith<$Res>(_self.account!, (value) {
return _then(_self.copyWith(account: value));
});
}/// Create a copy of SnRelationship
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAccountCopyWith<$Res> get related {
return $SnAccountCopyWith<$Res>(_self.related, (value) {
$SnAccountCopyWith<$Res>? get related {
if (_self.related == null) {
return null;
}
return $SnAccountCopyWith<$Res>(_self.related!, (value) {
return _then(_self.copyWith(related: value));
});
}
@@ -176,7 +183,7 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount account, String relatedId, SnAccount related, DateTime? expiredAt, int status)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount? account, String relatedId, SnAccount? related, DateTime? expiredAt, int status)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SnRelationship() when $default != null:
return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.accountId,_that.account,_that.relatedId,_that.related,_that.expiredAt,_that.status);case _:
@@ -197,7 +204,7 @@ return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.accountId,
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount account, String relatedId, SnAccount related, DateTime? expiredAt, int status) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount? account, String relatedId, SnAccount? related, DateTime? expiredAt, int status) $default,) {final _that = this;
switch (_that) {
case _SnRelationship():
return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.accountId,_that.account,_that.relatedId,_that.related,_that.expiredAt,_that.status);}
@@ -214,7 +221,7 @@ return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.accountId,
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount account, String relatedId, SnAccount related, DateTime? expiredAt, int status)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount? account, String relatedId, SnAccount? related, DateTime? expiredAt, int status)? $default,) {final _that = this;
switch (_that) {
case _SnRelationship() when $default != null:
return $default(_that.createdAt,_that.updatedAt,_that.deletedAt,_that.accountId,_that.account,_that.relatedId,_that.related,_that.expiredAt,_that.status);case _:
@@ -236,9 +243,10 @@ class _SnRelationship implements SnRelationship {
@override final DateTime? updatedAt;
@override final DateTime? deletedAt;
@override final String accountId;
@override final SnAccount account;
// Usually the account was not included in the response
@override final SnAccount? account;
@override final String relatedId;
@override final SnAccount related;
@override final SnAccount? related;
@override final DateTime? expiredAt;
@override final int status;
@@ -275,11 +283,11 @@ abstract mixin class _$SnRelationshipCopyWith<$Res> implements $SnRelationshipCo
factory _$SnRelationshipCopyWith(_SnRelationship value, $Res Function(_SnRelationship) _then) = __$SnRelationshipCopyWithImpl;
@override @useResult
$Res call({
DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount account, String relatedId, SnAccount related, DateTime? expiredAt, int status
DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String accountId, SnAccount? account, String relatedId, SnAccount? related, DateTime? expiredAt, int status
});
@override $SnAccountCopyWith<$Res> get account;@override $SnAccountCopyWith<$Res> get related;
@override $SnAccountCopyWith<$Res>? get account;@override $SnAccountCopyWith<$Res>? get related;
}
/// @nodoc
@@ -292,16 +300,16 @@ class __$SnRelationshipCopyWithImpl<$Res>
/// Create a copy of SnRelationship
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? accountId = null,Object? account = null,Object? relatedId = null,Object? related = null,Object? expiredAt = freezed,Object? status = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? accountId = null,Object? account = freezed,Object? relatedId = null,Object? related = freezed,Object? expiredAt = freezed,Object? status = null,}) {
return _then(_SnRelationship(
createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
as String,account: null == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
as SnAccount,relatedId: null == relatedId ? _self.relatedId : relatedId // ignore: cast_nullable_to_non_nullable
as String,related: null == related ? _self.related : related // ignore: cast_nullable_to_non_nullable
as SnAccount,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
as String,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
as SnAccount?,relatedId: null == relatedId ? _self.relatedId : relatedId // ignore: cast_nullable_to_non_nullable
as String,related: freezed == related ? _self.related : related // ignore: cast_nullable_to_non_nullable
as SnAccount?,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
as DateTime?,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as int,
));
@@ -311,18 +319,24 @@ as int,
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAccountCopyWith<$Res> get account {
return $SnAccountCopyWith<$Res>(_self.account, (value) {
$SnAccountCopyWith<$Res>? get account {
if (_self.account == null) {
return null;
}
return $SnAccountCopyWith<$Res>(_self.account!, (value) {
return _then(_self.copyWith(account: value));
});
}/// Create a copy of SnRelationship
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAccountCopyWith<$Res> get related {
return $SnAccountCopyWith<$Res>(_self.related, (value) {
$SnAccountCopyWith<$Res>? get related {
if (_self.related == null) {
return null;
}
return $SnAccountCopyWith<$Res>(_self.related!, (value) {
return _then(_self.copyWith(related: value));
});
}

View File

@@ -18,9 +18,13 @@ _SnRelationship _$SnRelationshipFromJson(Map<String, dynamic> json) =>
? null
: DateTime.parse(json['deleted_at'] as String),
accountId: json['account_id'] as String,
account: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
account: json['account'] == null
? null
: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
relatedId: json['related_id'] as String,
related: SnAccount.fromJson(json['related'] as Map<String, dynamic>),
related: json['related'] == null
? null
: SnAccount.fromJson(json['related'] as Map<String, dynamic>),
expiredAt: json['expired_at'] == null
? null
: DateTime.parse(json['expired_at'] as String),
@@ -33,9 +37,9 @@ Map<String, dynamic> _$SnRelationshipToJson(_SnRelationship instance) =>
'updated_at': instance.updatedAt?.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
'account_id': instance.accountId,
'account': instance.account.toJson(),
'account': instance.account?.toJson(),
'related_id': instance.relatedId,
'related': instance.related.toJson(),
'related': instance.related?.toJson(),
'expired_at': instance.expiredAt?.toIso8601String(),
'status': instance.status,
};

View File

@@ -32,10 +32,10 @@ final List<RouteItem> kAvailableRoutes = [
icon: Symbols.explore,
),
RouteItem(
name: 'searchPosts'.tr(),
path: '/posts/search',
description: 'searchPostsDescription'.tr(),
searchableAliases: ['search', 'posts'],
name: 'universalSearch'.tr(),
path: '/search',
description: 'universalSearchDescription'.tr(),
searchableAliases: ['search', 'universal', 'fediverse'],
icon: Symbols.search,
),
RouteItem(

View File

@@ -10,7 +10,7 @@ part of 'activity_rpc.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(presenceActivities)
const presenceActivitiesProvider = PresenceActivitiesFamily._();
final presenceActivitiesProvider = PresenceActivitiesFamily._();
final class PresenceActivitiesProvider
extends
@@ -22,7 +22,7 @@ final class PresenceActivitiesProvider
with
$FutureModifier<List<SnPresenceActivity>>,
$FutureProvider<List<SnPresenceActivity>> {
const PresenceActivitiesProvider._({
PresenceActivitiesProvider._({
required PresenceActivitiesFamily super.from,
required String super.argument,
}) : super(
@@ -71,7 +71,7 @@ String _$presenceActivitiesHash() =>
final class PresenceActivitiesFamily extends $Family
with $FunctionalFamilyOverride<FutureOr<List<SnPresenceActivity>>, String> {
const PresenceActivitiesFamily._()
PresenceActivitiesFamily._()
: super(
retry: null,
name: r'presenceActivitiesProvider',

View File

@@ -10,7 +10,7 @@ final articleDetailProvider = FutureProvider.autoDispose
try {
final response = await dio.get<Map<String, dynamic>>(
'/sphere/feeds/articles/$articleId',
'/insight/feeds/articles/$articleId',
);
if (response.statusCode == 200 && response.data != null) {

View File

@@ -245,7 +245,7 @@ class CallNotifier extends _$CallNotifier {
final apiClient = ref.read(apiClientProvider);
final ongoingCall = await ref.read(ongoingCallProvider(roomId).future);
final response = await apiClient.get(
'/sphere/chat/realtime/$roomId/join',
'/messager/chat/realtime/$roomId/join',
);
if (response.statusCode == 200 && response.data != null) {
final data = response.data;

View File

@@ -10,11 +10,11 @@ part of 'call.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(CallNotifier)
const callProvider = CallNotifierProvider._();
final callProvider = CallNotifierProvider._();
final class CallNotifierProvider
extends $NotifierProvider<CallNotifier, CallState> {
const CallNotifierProvider._()
CallNotifierProvider._()
: super(
from: null,
argument: null,
@@ -41,14 +41,13 @@ final class CallNotifierProvider
}
}
String _$callNotifierHash() => r'40bd884d3918b8e817329589c921774ab3c62ea2';
String _$callNotifierHash() => r'caa03913d98c6d98448af44059db5ef72b5d58f6';
abstract class _$CallNotifier extends $Notifier<CallState> {
CallState build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<CallState, CallState>;
final element =
ref.element
@@ -58,6 +57,6 @@ abstract class _$CallNotifier extends $Notifier<CallState> {
Object?,
Object?
>;
element.handleValue(ref, created);
element.handleCreate(ref, build);
}
}

View File

@@ -15,7 +15,7 @@ class ChatOnlineCountNotifier extends _$ChatOnlineCountNotifier {
// Fetch initial online count
final response = await apiClient.get(
'/sphere/chat/$chatroomId/members/online',
'/messager/chat/$chatroomId/members/online',
);
final initialCount = response.data as int;

View File

@@ -10,11 +10,11 @@ part of 'chat_online_count.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(ChatOnlineCountNotifier)
const chatOnlineCountProvider = ChatOnlineCountNotifierFamily._();
final chatOnlineCountProvider = ChatOnlineCountNotifierFamily._();
final class ChatOnlineCountNotifierProvider
extends $AsyncNotifierProvider<ChatOnlineCountNotifier, int> {
const ChatOnlineCountNotifierProvider._({
ChatOnlineCountNotifierProvider._({
required ChatOnlineCountNotifierFamily super.from,
required String super.argument,
}) : super(
@@ -52,7 +52,7 @@ final class ChatOnlineCountNotifierProvider
}
String _$chatOnlineCountNotifierHash() =>
r'19af8fd0e9f62c65e12a68215406776085235fa3';
r'b2f9f17bfece1937ec90590b8f11db2bec923156';
final class ChatOnlineCountNotifierFamily extends $Family
with
@@ -63,7 +63,7 @@ final class ChatOnlineCountNotifierFamily extends $Family
FutureOr<int>,
String
> {
const ChatOnlineCountNotifierFamily._()
ChatOnlineCountNotifierFamily._()
: super(
retry: null,
name: r'chatOnlineCountProvider',
@@ -87,7 +87,6 @@ abstract class _$ChatOnlineCountNotifier extends $AsyncNotifier<int> {
@$mustCallSuper
@override
void runBuild() {
final created = build(_$args);
final ref = this.ref as $Ref<AsyncValue<int>, int>;
final element =
ref.element
@@ -97,6 +96,6 @@ abstract class _$ChatOnlineCountNotifier extends $AsyncNotifier<int> {
Object?,
Object?
>;
element.handleValue(ref, created);
element.handleCreate(ref, () => build(_$args));
}
}

View File

@@ -105,7 +105,7 @@ class ChatRoomJoinedNotifier extends _$ChatRoomJoinedNotifier {
Future(() async {
try {
final client = ref.read(apiClientProvider);
final resp = await client.get('/sphere/chat');
final resp = await client.get('/messager/chat');
final remoteRooms = resp.data
.map((e) => SnChatRoom.fromJson(e))
.cast<SnChatRoom>()
@@ -122,7 +122,7 @@ class ChatRoomJoinedNotifier extends _$ChatRoomJoinedNotifier {
// Fallback to API
final client = ref.watch(apiClientProvider);
final resp = await client.get('/sphere/chat');
final resp = await client.get('/messager/chat');
final rooms = resp.data
.map((e) => SnChatRoom.fromJson(e))
.cast<SnChatRoom>()
@@ -306,7 +306,7 @@ class ChatRoomNotifier extends _$ChatRoomNotifier {
Future(() async {
try {
final client = ref.read(apiClientProvider);
final resp = await client.get('/sphere/chat/$identifier');
final resp = await client.get('/messager/chat/$identifier');
final remoteRoom = SnChatRoom.fromJson(resp.data);
// Update state with fresh data directly without saving to DB
// DB will be updated by ChatRoomJoinedNotifier's full sync
@@ -321,7 +321,7 @@ class ChatRoomNotifier extends _$ChatRoomNotifier {
// Fallback to API
try {
final client = ref.watch(apiClientProvider);
final resp = await client.get('/sphere/chat/$identifier');
final resp = await client.get('/messager/chat/$identifier');
final room = SnChatRoom.fromJson(resp.data);
await db.saveChatRooms([room]);
return room;
@@ -375,7 +375,7 @@ class ChatRoomIdentityNotifier extends _$ChatRoomIdentityNotifier {
try {
final client = ref.read(apiClientProvider);
final resp = await client.get(
'/sphere/chat/$identifier/members/me',
'/messager/chat/$identifier/members/me',
);
final remoteMember = SnChatMember.fromJson(resp.data);
await db.saveMember(remoteMember);
@@ -396,7 +396,7 @@ class ChatRoomIdentityNotifier extends _$ChatRoomIdentityNotifier {
// Fallback to API
try {
final client = ref.watch(apiClientProvider);
final resp = await client.get('/sphere/chat/$identifier/members/me');
final resp = await client.get('/messager/chat/$identifier/members/me');
final member = SnChatMember.fromJson(resp.data);
await db.saveMember(member);
return member;
@@ -444,7 +444,7 @@ class ChatRoomIdentityNotifier extends _$ChatRoomIdentityNotifier {
@riverpod
Future<List<SnChatMember>> chatroomInvites(Ref ref) async {
final client = ref.watch(apiClientProvider);
final resp = await client.get('/sphere/chat/invites');
final resp = await client.get('/messager/chat/invites');
return resp.data
.map((e) => SnChatMember.fromJson(e))
.cast<SnChatMember>()

View File

@@ -10,11 +10,11 @@ part of 'chat_room.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(ChatRoomJoinedNotifier)
const chatRoomJoinedProvider = ChatRoomJoinedNotifierProvider._();
final chatRoomJoinedProvider = ChatRoomJoinedNotifierProvider._();
final class ChatRoomJoinedNotifierProvider
extends $AsyncNotifierProvider<ChatRoomJoinedNotifier, List<SnChatRoom>> {
const ChatRoomJoinedNotifierProvider._()
ChatRoomJoinedNotifierProvider._()
: super(
from: null,
argument: null,
@@ -34,7 +34,7 @@ final class ChatRoomJoinedNotifierProvider
}
String _$chatRoomJoinedNotifierHash() =>
r'e69955be56ef2c04a8062a8a65925e0a23bfcbaa';
r'b3726e10298b99a8529c5e28a5c402b95016f096';
abstract class _$ChatRoomJoinedNotifier
extends $AsyncNotifier<List<SnChatRoom>> {
@@ -42,7 +42,6 @@ abstract class _$ChatRoomJoinedNotifier
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref =
this.ref as $Ref<AsyncValue<List<SnChatRoom>>, List<SnChatRoom>>;
final element =
@@ -53,16 +52,16 @@ abstract class _$ChatRoomJoinedNotifier
Object?,
Object?
>;
element.handleValue(ref, created);
element.handleCreate(ref, build);
}
}
@ProviderFor(ChatRoomNotifier)
const chatRoomProvider = ChatRoomNotifierFamily._();
final chatRoomProvider = ChatRoomNotifierFamily._();
final class ChatRoomNotifierProvider
extends $AsyncNotifierProvider<ChatRoomNotifier, SnChatRoom?> {
const ChatRoomNotifierProvider._({
ChatRoomNotifierProvider._({
required ChatRoomNotifierFamily super.from,
required String? super.argument,
}) : super(
@@ -98,7 +97,7 @@ final class ChatRoomNotifierProvider
}
}
String _$chatRoomNotifierHash() => r'1e6391e2ab4eeb114fa001aaa6b06ab2bd646f38';
String _$chatRoomNotifierHash() => r'9f7a8bdd4af381c6b60e65e74363a0af3c1a650e';
final class ChatRoomNotifierFamily extends $Family
with
@@ -109,7 +108,7 @@ final class ChatRoomNotifierFamily extends $Family
FutureOr<SnChatRoom?>,
String?
> {
const ChatRoomNotifierFamily._()
ChatRoomNotifierFamily._()
: super(
retry: null,
name: r'chatRoomProvider',
@@ -133,7 +132,6 @@ abstract class _$ChatRoomNotifier extends $AsyncNotifier<SnChatRoom?> {
@$mustCallSuper
@override
void runBuild() {
final created = build(_$args);
final ref = this.ref as $Ref<AsyncValue<SnChatRoom?>, SnChatRoom?>;
final element =
ref.element
@@ -143,16 +141,16 @@ abstract class _$ChatRoomNotifier extends $AsyncNotifier<SnChatRoom?> {
Object?,
Object?
>;
element.handleValue(ref, created);
element.handleCreate(ref, () => build(_$args));
}
}
@ProviderFor(ChatRoomIdentityNotifier)
const chatRoomIdentityProvider = ChatRoomIdentityNotifierFamily._();
final chatRoomIdentityProvider = ChatRoomIdentityNotifierFamily._();
final class ChatRoomIdentityNotifierProvider
extends $AsyncNotifierProvider<ChatRoomIdentityNotifier, SnChatMember?> {
const ChatRoomIdentityNotifierProvider._({
ChatRoomIdentityNotifierProvider._({
required ChatRoomIdentityNotifierFamily super.from,
required String? super.argument,
}) : super(
@@ -190,7 +188,7 @@ final class ChatRoomIdentityNotifierProvider
}
String _$chatRoomIdentityNotifierHash() =>
r'27c17d55366d39be81d7209837e5c01f80a68a24';
r'1ce75462a19cc037c97ee6084a30fee1f5335875';
final class ChatRoomIdentityNotifierFamily extends $Family
with
@@ -201,7 +199,7 @@ final class ChatRoomIdentityNotifierFamily extends $Family
FutureOr<SnChatMember?>,
String?
> {
const ChatRoomIdentityNotifierFamily._()
ChatRoomIdentityNotifierFamily._()
: super(
retry: null,
name: r'chatRoomIdentityProvider',
@@ -226,7 +224,6 @@ abstract class _$ChatRoomIdentityNotifier
@$mustCallSuper
@override
void runBuild() {
final created = build(_$args);
final ref = this.ref as $Ref<AsyncValue<SnChatMember?>, SnChatMember?>;
final element =
ref.element
@@ -236,12 +233,12 @@ abstract class _$ChatRoomIdentityNotifier
Object?,
Object?
>;
element.handleValue(ref, created);
element.handleCreate(ref, () => build(_$args));
}
}
@ProviderFor(chatroomInvites)
const chatroomInvitesProvider = ChatroomInvitesProvider._();
final chatroomInvitesProvider = ChatroomInvitesProvider._();
final class ChatroomInvitesProvider
extends
@@ -253,7 +250,7 @@ final class ChatroomInvitesProvider
with
$FutureModifier<List<SnChatMember>>,
$FutureProvider<List<SnChatMember>> {
const ChatroomInvitesProvider._()
ChatroomInvitesProvider._()
: super(
from: null,
argument: null,
@@ -279,4 +276,4 @@ final class ChatroomInvitesProvider
}
}
String _$chatroomInvitesHash() => r'5cd6391b09c5517ede19bacce43b45c8d71dd087';
String _$chatroomInvitesHash() => r'fc23231d5f111b1c3796ffae2b471384b951861a';

View File

@@ -7,6 +7,7 @@ import "package:island/pods/chat/chat_room.dart";
import "package:island/pods/lifecycle.dart";
import "package:island/pods/chat/messages_notifier.dart";
import "package:island/pods/websocket.dart";
import "package:island/talker.dart";
import "package:island/widgets/chat/call_button.dart";
import "package:riverpod_annotation/riverpod_annotation.dart";
@@ -35,6 +36,22 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
Timer? _typingCooldownTimer;
Timer? _periodicSubscribeTimer;
StreamSubscription? _wsSubscription;
Function? _sendMessage;
void _cleanupResources() {
if (_wsSubscription != null) {
_wsSubscription!.cancel();
_wsSubscription = null;
}
if (_typingCleanupTimer != null) {
_typingCleanupTimer!.cancel();
_typingCleanupTimer = null;
}
if (_periodicSubscribeTimer != null) {
_periodicSubscribeTimer!.cancel();
_periodicSubscribeTimer = null;
}
}
@override
List<SnChatMember> build(String roomId) {
@@ -43,6 +60,8 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
final chatIdentityAsync = ref.watch(chatRoomIdentityProvider(roomId));
_messagesNotifier = ref.watch(messagesProvider(roomId).notifier);
_cleanupResources();
if (chatRoomAsync.isLoading || chatIdentityAsync.isLoading) {
return [];
}
@@ -56,12 +75,14 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
// Subscribe to messages
final wsState = ref.read(websocketStateProvider.notifier);
wsState.sendMessage(
_sendMessage = wsState.sendMessage;
talker.info('[MessageSubscriber] Subscribing room $roomId');
_sendMessage!(
jsonEncode(
WebSocketPacket(
type: 'messages.subscribe',
data: {'chat_room_id': roomId},
endpoint: 'sphere',
endpoint: 'messager',
),
),
);
@@ -93,64 +114,85 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
// Set up periodic subscribe timer (every 5 minutes)
_periodicSubscribeTimer = Timer.periodic(const Duration(minutes: 5), (_) {
wsState.sendMessage(
_sendMessage!(
jsonEncode(
WebSocketPacket(
type: 'messages.subscribe',
data: {'chat_room_id': roomId},
endpoint: 'sphere',
endpoint: 'messager',
),
),
);
});
// Listen to app lifecycle changes
ref.listen(appLifecycleStateProvider, (previous, next) {
final lifecycleState = next.value;
if (lifecycleState == AppLifecycleState.paused ||
lifecycleState == AppLifecycleState.inactive) {
// Unsubscribe when app goes to background
final wsState = ref.read(websocketStateProvider.notifier);
wsState.sendMessage(
talker.info('[MessageSubscriber] Unsubscribing room $roomId');
_sendMessage!(
jsonEncode(
WebSocketPacket(
type: 'messages.unsubscribe',
data: {'chat_room_id': roomId},
endpoint: 'sphere',
endpoint: 'messager',
),
),
);
} else if (lifecycleState == AppLifecycleState.resumed) {
// Resubscribe when app comes back to foreground
final wsState = ref.read(websocketStateProvider.notifier);
wsState.sendMessage(
talker.info('[MessageSubscriber] Subscribing room $roomId');
_sendMessage!(
jsonEncode(
WebSocketPacket(
type: 'messages.subscribe',
data: {'chat_room_id': roomId},
endpoint: 'sphere',
endpoint: 'messager',
),
),
);
}
});
// Cleanup on dispose
ref.onDispose(() {
ref.read(currentSubscribedChatIdProvider.notifier).set(null);
wsState.sendMessage(
jsonEncode(
WebSocketPacket(
type: 'messages.unsubscribe',
data: {'chat_room_id': roomId},
endpoint: 'sphere',
final subscribedNotifier = ref.watch(
currentSubscribedChatIdProvider.notifier,
);
ref.onCancel(() {
talker.info('[MessageSubscriber] Unsubscribing room $roomId');
subscribedNotifier.set(null);
try {
_sendMessage!(
jsonEncode(
WebSocketPacket(
type: 'messages.unsubscribe',
data: {'chat_room_id': roomId},
endpoint: 'messager',
),
),
),
);
_wsSubscription?.cancel();
_typingCleanupTimer?.cancel();
_typingCooldownTimer?.cancel();
_periodicSubscribeTimer?.cancel();
);
} catch (e, stackTrace) {
talker.error(
'[MessageSubscriber] Error sending unsubscribe message for room $roomId: $e\n$stackTrace',
);
}
try {
_cleanupResources();
} catch (e, stackTrace) {
talker.error(
'[MessageSubscriber] Error during cleanup for room $roomId: $e\n$stackTrace',
);
}
try {
if (_typingCooldownTimer != null) {
_typingCooldownTimer!.cancel();
}
} catch (e, stackTrace) {
talker.error(
'[MessageSubscriber] Error cancelling typing cooldown timer for room $roomId: $e\n$stackTrace',
);
}
});
return _typingStatuses;
@@ -201,13 +243,13 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
void sendReadReceipt() {
// Send websocket packet
final wsState = ref.read(websocketStateProvider.notifier);
wsState.sendMessage(
if (_sendMessage == null) return;
_sendMessage!(
jsonEncode(
WebSocketPacket(
type: 'messages.read',
data: {'chat_room_id': roomId},
endpoint: 'sphere',
endpoint: 'messager',
),
),
);
@@ -218,13 +260,13 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
if (_typingCooldownTimer != null) return;
// Send typing status immediately
final wsState = ref.read(websocketStateProvider.notifier);
wsState.sendMessage(
if (_sendMessage == null) return;
_sendMessage!(
jsonEncode(
WebSocketPacket(
type: 'messages.typing',
data: {'chat_room_id': roomId},
endpoint: 'sphere',
endpoint: 'messager',
),
),
);

View File

@@ -10,11 +10,11 @@ part of 'chat_subscribe.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(ChatSubscribeNotifier)
const chatSubscribeProvider = ChatSubscribeNotifierFamily._();
final chatSubscribeProvider = ChatSubscribeNotifierFamily._();
final class ChatSubscribeNotifierProvider
extends $NotifierProvider<ChatSubscribeNotifier, List<SnChatMember>> {
const ChatSubscribeNotifierProvider._({
ChatSubscribeNotifierProvider._({
required ChatSubscribeNotifierFamily super.from,
required String super.argument,
}) : super(
@@ -59,7 +59,7 @@ final class ChatSubscribeNotifierProvider
}
String _$chatSubscribeNotifierHash() =>
r'1aa164429aaab1628b5edbae11e33b0860abdcdc';
r'b7624ae45ace2944a88f8b4d14ddce556c236371';
final class ChatSubscribeNotifierFamily extends $Family
with
@@ -70,7 +70,7 @@ final class ChatSubscribeNotifierFamily extends $Family
List<SnChatMember>,
String
> {
const ChatSubscribeNotifierFamily._()
ChatSubscribeNotifierFamily._()
: super(
retry: null,
name: r'chatSubscribeProvider',
@@ -94,7 +94,6 @@ abstract class _$ChatSubscribeNotifier extends $Notifier<List<SnChatMember>> {
@$mustCallSuper
@override
void runBuild() {
final created = build(_$args);
final ref = this.ref as $Ref<List<SnChatMember>, List<SnChatMember>>;
final element =
ref.element
@@ -104,6 +103,6 @@ abstract class _$ChatSubscribeNotifier extends $Notifier<List<SnChatMember>> {
Object?,
Object?
>;
element.handleValue(ref, created);
element.handleCreate(ref, () => build(_$args));
}
}

View File

@@ -24,7 +24,7 @@ class ChatUnreadCountNotifier extends _$ChatUnreadCountNotifier {
try {
final client = ref.read(apiClientProvider);
final response = await client.get('/sphere/chat/unread');
final response = await client.get('/messager/chat/unread');
return (response.data as num).toInt();
} catch (_) {
return 0;
@@ -65,7 +65,7 @@ class ChatSummary extends _$ChatSummary {
@override
Future<Map<String, SnChatSummary>> build() async {
final client = ref.watch(apiClientProvider);
final resp = await client.get('/sphere/chat/summary');
final resp = await client.get('/messager/chat/summary');
final Map<String, dynamic> data = resp.data;
final summaries = data.map(

View File

@@ -10,11 +10,11 @@ part of 'chat_summary.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(ChatUnreadCountNotifier)
const chatUnreadCountProvider = ChatUnreadCountNotifierProvider._();
final chatUnreadCountProvider = ChatUnreadCountNotifierProvider._();
final class ChatUnreadCountNotifierProvider
extends $AsyncNotifierProvider<ChatUnreadCountNotifier, int> {
const ChatUnreadCountNotifierProvider._()
ChatUnreadCountNotifierProvider._()
: super(
from: null,
argument: null,
@@ -34,14 +34,13 @@ final class ChatUnreadCountNotifierProvider
}
String _$chatUnreadCountNotifierHash() =>
r'b8d93589dc37f772d4c3a07d9afd81c37026e57d';
r'169b28f8759ebd9de75f7de17f60d493737ee7a8';
abstract class _$ChatUnreadCountNotifier extends $AsyncNotifier<int> {
FutureOr<int> build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<AsyncValue<int>, int>;
final element =
ref.element
@@ -51,16 +50,16 @@ abstract class _$ChatUnreadCountNotifier extends $AsyncNotifier<int> {
Object?,
Object?
>;
element.handleValue(ref, created);
element.handleCreate(ref, build);
}
}
@ProviderFor(ChatSummary)
const chatSummaryProvider = ChatSummaryProvider._();
final chatSummaryProvider = ChatSummaryProvider._();
final class ChatSummaryProvider
extends $AsyncNotifierProvider<ChatSummary, Map<String, SnChatSummary>> {
const ChatSummaryProvider._()
ChatSummaryProvider._()
: super(
from: null,
argument: null,
@@ -79,7 +78,7 @@ final class ChatSummaryProvider
ChatSummary create() => ChatSummary();
}
String _$chatSummaryHash() => r'dfa5e487586482ebdafef8d711f74db68ee86f84';
String _$chatSummaryHash() => r'82f516d4ce8b67dadb815523df57a3c30a33ef91';
abstract class _$ChatSummary
extends $AsyncNotifier<Map<String, SnChatSummary>> {
@@ -87,7 +86,6 @@ abstract class _$ChatSummary
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref =
this.ref
as $Ref<
@@ -105,6 +103,6 @@ abstract class _$ChatSummary
Object?,
Object?
>;
element.handleValue(ref, created);
element.handleCreate(ref, build);
}
}

View File

@@ -50,9 +50,6 @@ class MessagesNotifier extends _$MessagesNotifier {
late Future<SnAccount?> Function(String) _fetchAccount;
// Disposal handling
bool _disposed = false;
@override
FutureOr<List<LocalChatMessage>> build(String roomId) async {
_apiClient = ref.watch(apiClientProvider);
@@ -81,17 +78,11 @@ class MessagesNotifier extends _$MessagesNotifier {
talker.log('MessagesNotifier built for room $roomId');
// Set up disposal handling
ref.onDispose(() {
_disposed = true;
talker.log('MessagesNotifier disposed for room $roomId');
});
// Only setup sync and lifecycle listeners if user is a member
if (identity != null) {
ref.listen(appLifecycleStateProvider, (_, next) {
next.whenData((state) {
if (_disposed) return; // Check disposal before accessing ref
if (!ref.mounted) return; // Check disposal before accessing ref
if (state == AppLifecycleState.paused) {
_lastPauseTime = DateTime.now();
talker.log('App paused, recording time');
@@ -100,7 +91,7 @@ class MessagesNotifier extends _$MessagesNotifier {
final diff = DateTime.now().difference(_lastPauseTime!);
if (diff > const Duration(minutes: 1)) {
talker.log('App resumed after >1 min, syncing messages');
if (!_disposed) {
if (!!ref.mounted) {
syncMessages(); // Check disposal before calling syncMessages
}
} else {
@@ -281,7 +272,7 @@ class MessagesNotifier extends _$MessagesNotifier {
talker.log('Fetching messages from API, offset $offset, take $take');
if (_totalCount == null) {
final response = await _apiClient.get(
'/sphere/chat/$roomId/messages',
'/messager/chat/$roomId/messages',
queryParameters: {'offset': 0, 'take': 1},
);
_totalCount = int.parse(response.headers['x-total']?.firstOrNull ?? '0');
@@ -293,7 +284,7 @@ class MessagesNotifier extends _$MessagesNotifier {
}
final response = await _apiClient.get(
'/sphere/chat/$roomId/messages',
'/messager/chat/$roomId/messages',
queryParameters: {'offset': offset, 'take': take},
);
@@ -336,7 +327,7 @@ class MessagesNotifier extends _$MessagesNotifier {
talker.log('Starting message sync');
// Use Future.microtask to set syncing state, but check disposal to avoid errors
Future.microtask(() {
if (!_disposed) {
if (!!ref.mounted) {
ref.read(chatSyncingProvider.notifier).set(true);
}
});
@@ -373,7 +364,7 @@ class MessagesNotifier extends _$MessagesNotifier {
do {
final resp = await _apiClient.post(
'/sphere/chat/${_room.id}/sync',
'/messager/chat/${_room.id}/sync',
data: {'last_sync_timestamp': lastSyncTimestamp},
);
@@ -514,7 +505,7 @@ class MessagesNotifier extends _$MessagesNotifier {
if (!_hasMore || state is AsyncLoading) return;
talker.log('Loading more messages');
if (!_disposed) {
if (!!ref.mounted) {
Future.microtask(() => ref.read(chatSyncingProvider.notifier).set(true));
}
try {
@@ -603,8 +594,8 @@ class MessagesNotifier extends _$MessagesNotifier {
final response = await _apiClient.request(
editingTo == null
? '/sphere/chat/$roomId/messages'
: '/sphere/chat/$roomId/messages/${editingTo.id}',
? '/messager/chat/$roomId/messages'
: '/messager/chat/$roomId/messages/${editingTo.id}',
data: {
'content': content,
'attachments_id': cloudAttachments.map((e) => e.id).toList(),
@@ -688,7 +679,7 @@ class MessagesNotifier extends _$MessagesNotifier {
try {
var remoteMessage = message.toRemoteMessage();
final response = await _apiClient.post(
'/sphere/chat/${message.roomId}/messages',
'/messager/chat/${message.roomId}/messages',
data: {
'content': remoteMessage.content,
'attachments_id': remoteMessage.attachments,
@@ -925,7 +916,7 @@ class MessagesNotifier extends _$MessagesNotifier {
}
try {
await _apiClient.delete('/sphere/chat/$roomId/messages/$messageId');
await _apiClient.delete('/messager/chat/$roomId/messages/$messageId');
await receiveMessageDeletion(messageId);
} catch (err, stackTrace) {
talker.log(
@@ -1033,7 +1024,7 @@ class MessagesNotifier extends _$MessagesNotifier {
}
final response = await _apiClient.get(
'/sphere/chat/$roomId/messages/$messageId',
'/messager/chat/$roomId/messages/$messageId',
);
final remoteMessage = SnChatMessage.fromJson(response.data);
final message = LocalChatMessage.fromRemoteMessage(
@@ -1058,7 +1049,7 @@ class MessagesNotifier extends _$MessagesNotifier {
_isJumping = true;
// Clear flashing messages when starting a new jump
if (!_disposed) {
if (!!ref.mounted) {
ref.read(flashingMessagesProvider.notifier).state = {};
}

View File

@@ -10,11 +10,11 @@ part of 'messages_notifier.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(MessagesNotifier)
const messagesProvider = MessagesNotifierFamily._();
final messagesProvider = MessagesNotifierFamily._();
final class MessagesNotifierProvider
extends $AsyncNotifierProvider<MessagesNotifier, List<LocalChatMessage>> {
const MessagesNotifierProvider._({
MessagesNotifierProvider._({
required MessagesNotifierFamily super.from,
required String super.argument,
}) : super(
@@ -50,7 +50,7 @@ final class MessagesNotifierProvider
}
}
String _$messagesNotifierHash() => r'c7e2cd7f5b8673af88f5076814393dbfbd0d43c5';
String _$messagesNotifierHash() => r'a721a4b92b48ee7c2289cdcd7130bbf1ca9dcb40';
final class MessagesNotifierFamily extends $Family
with
@@ -61,7 +61,7 @@ final class MessagesNotifierFamily extends $Family
FutureOr<List<LocalChatMessage>>,
String
> {
const MessagesNotifierFamily._()
MessagesNotifierFamily._()
: super(
retry: null,
name: r'messagesProvider',
@@ -86,7 +86,6 @@ abstract class _$MessagesNotifier
@$mustCallSuper
@override
void runBuild() {
final created = build(_$args);
final ref =
this.ref
as $Ref<AsyncValue<List<LocalChatMessage>>, List<LocalChatMessage>>;
@@ -101,6 +100,6 @@ abstract class _$MessagesNotifier
Object?,
Object?
>;
element.handleValue(ref, created);
element.handleCreate(ref, () => build(_$args));
}
}

View File

@@ -40,6 +40,7 @@ const kAppFirstLaunchAt = 'app_first_launch_at';
const kAppAskedReview = 'app_asked_review';
const kAppDashSearchEngine = 'app_dash_search_engine';
const kAppDefaultScreen = 'app_default_screen';
const kAppShowFediverseContent = 'app_show_fediverse_content';
// Will be overrided by the ProviderScope
final sharedPreferencesProvider = Provider<SharedPreferences>((ref) {
@@ -91,6 +92,7 @@ sealed class AppSettings with _$AppSettings {
required bool askedReview,
required String? dashSearchEngine,
required String? defaultScreen,
required bool showFediverseContent,
}) = _AppSettings;
}
@@ -122,6 +124,7 @@ class AppSettingsNotifier extends _$AppSettingsNotifier {
firstLaunchAt: prefs.getString(kAppFirstLaunchAt),
dashSearchEngine: prefs.getString(kAppDashSearchEngine),
defaultScreen: prefs.getString(kAppDefaultScreen),
showFediverseContent: prefs.getBool(kAppShowFediverseContent) ?? true,
);
}
@@ -311,6 +314,12 @@ class AppSettingsNotifier extends _$AppSettingsNotifier {
}
state = state.copyWith(dashSearchEngine: value);
}
void setShowFediverseContent(bool value) {
final prefs = ref.read(sharedPreferencesProvider);
prefs.setBool(kAppShowFediverseContent, value);
state = state.copyWith(showFediverseContent: value);
}
}
final updateInfoProvider =

View File

@@ -290,7 +290,7 @@ mixin _$AppSettings {
ThemeColors? get customColors; Size? get windowSize;// The window size for desktop platforms
double get windowOpacity;// The window opacity for desktop platforms
double get cardTransparency;// The card background opacity
String? get defaultPoolId; String get messageDisplayStyle; String? get themeMode; bool get disableAnimation; bool get groupedChatList; String? get firstLaunchAt; bool get askedReview; String? get dashSearchEngine; String? get defaultScreen;
String? get defaultPoolId; String get messageDisplayStyle; String? get themeMode; bool get disableAnimation; bool get groupedChatList; String? get firstLaunchAt; bool get askedReview; String? get dashSearchEngine; String? get defaultScreen; bool get showFediverseContent;
/// Create a copy of AppSettings
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -301,16 +301,16 @@ $AppSettingsCopyWith<AppSettings> get copyWith => _$AppSettingsCopyWithImpl<AppS
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppSettings&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.festivalFeatures, festivalFeatures) || other.festivalFeatures == festivalFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.notifyWithHaptic, notifyWithHaptic) || other.notifyWithHaptic == notifyWithHaptic)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.customColors, customColors) || other.customColors == customColors)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.cardTransparency, cardTransparency) || other.cardTransparency == cardTransparency)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.disableAnimation, disableAnimation) || other.disableAnimation == disableAnimation)&&(identical(other.groupedChatList, groupedChatList) || other.groupedChatList == groupedChatList)&&(identical(other.firstLaunchAt, firstLaunchAt) || other.firstLaunchAt == firstLaunchAt)&&(identical(other.askedReview, askedReview) || other.askedReview == askedReview)&&(identical(other.dashSearchEngine, dashSearchEngine) || other.dashSearchEngine == dashSearchEngine)&&(identical(other.defaultScreen, defaultScreen) || other.defaultScreen == defaultScreen));
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppSettings&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.festivalFeatures, festivalFeatures) || other.festivalFeatures == festivalFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.notifyWithHaptic, notifyWithHaptic) || other.notifyWithHaptic == notifyWithHaptic)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.customColors, customColors) || other.customColors == customColors)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.cardTransparency, cardTransparency) || other.cardTransparency == cardTransparency)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.disableAnimation, disableAnimation) || other.disableAnimation == disableAnimation)&&(identical(other.groupedChatList, groupedChatList) || other.groupedChatList == groupedChatList)&&(identical(other.firstLaunchAt, firstLaunchAt) || other.firstLaunchAt == firstLaunchAt)&&(identical(other.askedReview, askedReview) || other.askedReview == askedReview)&&(identical(other.dashSearchEngine, dashSearchEngine) || other.dashSearchEngine == dashSearchEngine)&&(identical(other.defaultScreen, defaultScreen) || other.defaultScreen == defaultScreen)&&(identical(other.showFediverseContent, showFediverseContent) || other.showFediverseContent == showFediverseContent));
}
@override
int get hashCode => Object.hashAll([runtimeType,dataSavingMode,soundEffects,festivalFeatures,enterToSend,appBarTransparent,showBackgroundImage,notifyWithHaptic,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,disableAnimation,groupedChatList,firstLaunchAt,askedReview,dashSearchEngine,defaultScreen]);
int get hashCode => Object.hashAll([runtimeType,dataSavingMode,soundEffects,festivalFeatures,enterToSend,appBarTransparent,showBackgroundImage,notifyWithHaptic,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,disableAnimation,groupedChatList,firstLaunchAt,askedReview,dashSearchEngine,defaultScreen,showFediverseContent]);
@override
String toString() {
return 'AppSettings(dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, festivalFeatures: $festivalFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, notifyWithHaptic: $notifyWithHaptic, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, disableAnimation: $disableAnimation, groupedChatList: $groupedChatList, firstLaunchAt: $firstLaunchAt, askedReview: $askedReview, dashSearchEngine: $dashSearchEngine, defaultScreen: $defaultScreen)';
return 'AppSettings(dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, festivalFeatures: $festivalFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, notifyWithHaptic: $notifyWithHaptic, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, disableAnimation: $disableAnimation, groupedChatList: $groupedChatList, firstLaunchAt: $firstLaunchAt, askedReview: $askedReview, dashSearchEngine: $dashSearchEngine, defaultScreen: $defaultScreen, showFediverseContent: $showFediverseContent)';
}
@@ -321,7 +321,7 @@ abstract mixin class $AppSettingsCopyWith<$Res> {
factory $AppSettingsCopyWith(AppSettings value, $Res Function(AppSettings) _then) = _$AppSettingsCopyWithImpl;
@useResult
$Res call({
bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen
bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen, bool showFediverseContent
});
@@ -338,7 +338,7 @@ class _$AppSettingsCopyWithImpl<$Res>
/// Create a copy of AppSettings
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? dataSavingMode = null,Object? soundEffects = null,Object? festivalFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? notifyWithHaptic = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? disableAnimation = null,Object? groupedChatList = null,Object? firstLaunchAt = freezed,Object? askedReview = null,Object? dashSearchEngine = freezed,Object? defaultScreen = freezed,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? dataSavingMode = null,Object? soundEffects = null,Object? festivalFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? notifyWithHaptic = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? disableAnimation = null,Object? groupedChatList = null,Object? firstLaunchAt = freezed,Object? askedReview = null,Object? dashSearchEngine = freezed,Object? defaultScreen = freezed,Object? showFediverseContent = null,}) {
return _then(_self.copyWith(
dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable
as bool,soundEffects: null == soundEffects ? _self.soundEffects : soundEffects // ignore: cast_nullable_to_non_nullable
@@ -362,7 +362,8 @@ as bool,firstLaunchAt: freezed == firstLaunchAt ? _self.firstLaunchAt : firstLau
as String?,askedReview: null == askedReview ? _self.askedReview : askedReview // ignore: cast_nullable_to_non_nullable
as bool,dashSearchEngine: freezed == dashSearchEngine ? _self.dashSearchEngine : dashSearchEngine // ignore: cast_nullable_to_non_nullable
as String?,defaultScreen: freezed == defaultScreen ? _self.defaultScreen : defaultScreen // ignore: cast_nullable_to_non_nullable
as String?,
as String?,showFediverseContent: null == showFediverseContent ? _self.showFediverseContent : showFediverseContent // ignore: cast_nullable_to_non_nullable
as bool,
));
}
/// Create a copy of AppSettings
@@ -456,10 +457,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen, bool showFediverseContent)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _AppSettings() when $default != null:
return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.disableAnimation,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine,_that.defaultScreen);case _:
return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.disableAnimation,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine,_that.defaultScreen,_that.showFediverseContent);case _:
return orElse();
}
@@ -477,10 +478,10 @@ return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen, bool showFediverseContent) $default,) {final _that = this;
switch (_that) {
case _AppSettings():
return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.disableAnimation,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine,_that.defaultScreen);}
return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.disableAnimation,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine,_that.defaultScreen,_that.showFediverseContent);}
}
/// A variant of `when` that fallback to returning `null`
///
@@ -494,10 +495,10 @@ return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen, bool showFediverseContent)? $default,) {final _that = this;
switch (_that) {
case _AppSettings() when $default != null:
return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.disableAnimation,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine,_that.defaultScreen);case _:
return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.disableAnimation,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine,_that.defaultScreen,_that.showFediverseContent);case _:
return null;
}
@@ -509,7 +510,7 @@ return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_
class _AppSettings implements AppSettings {
const _AppSettings({required this.dataSavingMode, required this.soundEffects, required this.festivalFeatures, required this.enterToSend, required this.appBarTransparent, required this.showBackgroundImage, required this.notifyWithHaptic, required this.customFonts, required this.appColorScheme, required this.customColors, required this.windowSize, required this.windowOpacity, required this.cardTransparency, required this.defaultPoolId, required this.messageDisplayStyle, required this.themeMode, required this.disableAnimation, required this.groupedChatList, required this.firstLaunchAt, required this.askedReview, required this.dashSearchEngine, required this.defaultScreen});
const _AppSettings({required this.dataSavingMode, required this.soundEffects, required this.festivalFeatures, required this.enterToSend, required this.appBarTransparent, required this.showBackgroundImage, required this.notifyWithHaptic, required this.customFonts, required this.appColorScheme, required this.customColors, required this.windowSize, required this.windowOpacity, required this.cardTransparency, required this.defaultPoolId, required this.messageDisplayStyle, required this.themeMode, required this.disableAnimation, required this.groupedChatList, required this.firstLaunchAt, required this.askedReview, required this.dashSearchEngine, required this.defaultScreen, required this.showFediverseContent});
@override final bool dataSavingMode;
@@ -538,6 +539,7 @@ class _AppSettings implements AppSettings {
@override final bool askedReview;
@override final String? dashSearchEngine;
@override final String? defaultScreen;
@override final bool showFediverseContent;
/// Create a copy of AppSettings
/// with the given fields replaced by the non-null parameter values.
@@ -549,16 +551,16 @@ _$AppSettingsCopyWith<_AppSettings> get copyWith => __$AppSettingsCopyWithImpl<_
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettings&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.festivalFeatures, festivalFeatures) || other.festivalFeatures == festivalFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.notifyWithHaptic, notifyWithHaptic) || other.notifyWithHaptic == notifyWithHaptic)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.customColors, customColors) || other.customColors == customColors)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.cardTransparency, cardTransparency) || other.cardTransparency == cardTransparency)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.disableAnimation, disableAnimation) || other.disableAnimation == disableAnimation)&&(identical(other.groupedChatList, groupedChatList) || other.groupedChatList == groupedChatList)&&(identical(other.firstLaunchAt, firstLaunchAt) || other.firstLaunchAt == firstLaunchAt)&&(identical(other.askedReview, askedReview) || other.askedReview == askedReview)&&(identical(other.dashSearchEngine, dashSearchEngine) || other.dashSearchEngine == dashSearchEngine)&&(identical(other.defaultScreen, defaultScreen) || other.defaultScreen == defaultScreen));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettings&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.festivalFeatures, festivalFeatures) || other.festivalFeatures == festivalFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.notifyWithHaptic, notifyWithHaptic) || other.notifyWithHaptic == notifyWithHaptic)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.customColors, customColors) || other.customColors == customColors)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.cardTransparency, cardTransparency) || other.cardTransparency == cardTransparency)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.disableAnimation, disableAnimation) || other.disableAnimation == disableAnimation)&&(identical(other.groupedChatList, groupedChatList) || other.groupedChatList == groupedChatList)&&(identical(other.firstLaunchAt, firstLaunchAt) || other.firstLaunchAt == firstLaunchAt)&&(identical(other.askedReview, askedReview) || other.askedReview == askedReview)&&(identical(other.dashSearchEngine, dashSearchEngine) || other.dashSearchEngine == dashSearchEngine)&&(identical(other.defaultScreen, defaultScreen) || other.defaultScreen == defaultScreen)&&(identical(other.showFediverseContent, showFediverseContent) || other.showFediverseContent == showFediverseContent));
}
@override
int get hashCode => Object.hashAll([runtimeType,dataSavingMode,soundEffects,festivalFeatures,enterToSend,appBarTransparent,showBackgroundImage,notifyWithHaptic,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,disableAnimation,groupedChatList,firstLaunchAt,askedReview,dashSearchEngine,defaultScreen]);
int get hashCode => Object.hashAll([runtimeType,dataSavingMode,soundEffects,festivalFeatures,enterToSend,appBarTransparent,showBackgroundImage,notifyWithHaptic,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,disableAnimation,groupedChatList,firstLaunchAt,askedReview,dashSearchEngine,defaultScreen,showFediverseContent]);
@override
String toString() {
return 'AppSettings(dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, festivalFeatures: $festivalFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, notifyWithHaptic: $notifyWithHaptic, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, disableAnimation: $disableAnimation, groupedChatList: $groupedChatList, firstLaunchAt: $firstLaunchAt, askedReview: $askedReview, dashSearchEngine: $dashSearchEngine, defaultScreen: $defaultScreen)';
return 'AppSettings(dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, festivalFeatures: $festivalFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, notifyWithHaptic: $notifyWithHaptic, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, disableAnimation: $disableAnimation, groupedChatList: $groupedChatList, firstLaunchAt: $firstLaunchAt, askedReview: $askedReview, dashSearchEngine: $dashSearchEngine, defaultScreen: $defaultScreen, showFediverseContent: $showFediverseContent)';
}
@@ -569,7 +571,7 @@ abstract mixin class _$AppSettingsCopyWith<$Res> implements $AppSettingsCopyWith
factory _$AppSettingsCopyWith(_AppSettings value, $Res Function(_AppSettings) _then) = __$AppSettingsCopyWithImpl;
@override @useResult
$Res call({
bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen
bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen, bool showFediverseContent
});
@@ -586,7 +588,7 @@ class __$AppSettingsCopyWithImpl<$Res>
/// Create a copy of AppSettings
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? dataSavingMode = null,Object? soundEffects = null,Object? festivalFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? notifyWithHaptic = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? disableAnimation = null,Object? groupedChatList = null,Object? firstLaunchAt = freezed,Object? askedReview = null,Object? dashSearchEngine = freezed,Object? defaultScreen = freezed,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? dataSavingMode = null,Object? soundEffects = null,Object? festivalFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? notifyWithHaptic = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? disableAnimation = null,Object? groupedChatList = null,Object? firstLaunchAt = freezed,Object? askedReview = null,Object? dashSearchEngine = freezed,Object? defaultScreen = freezed,Object? showFediverseContent = null,}) {
return _then(_AppSettings(
dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable
as bool,soundEffects: null == soundEffects ? _self.soundEffects : soundEffects // ignore: cast_nullable_to_non_nullable
@@ -610,7 +612,8 @@ as bool,firstLaunchAt: freezed == firstLaunchAt ? _self.firstLaunchAt : firstLau
as String?,askedReview: null == askedReview ? _self.askedReview : askedReview // ignore: cast_nullable_to_non_nullable
as bool,dashSearchEngine: freezed == dashSearchEngine ? _self.dashSearchEngine : dashSearchEngine // ignore: cast_nullable_to_non_nullable
as String?,defaultScreen: freezed == defaultScreen ? _self.defaultScreen : defaultScreen // ignore: cast_nullable_to_non_nullable
as String?,
as String?,showFediverseContent: null == showFediverseContent ? _self.showFediverseContent : showFediverseContent // ignore: cast_nullable_to_non_nullable
as bool,
));
}

View File

@@ -33,11 +33,11 @@ Map<String, dynamic> _$ThemeColorsToJson(_ThemeColors instance) =>
// ignore_for_file: type=lint, type=warning
@ProviderFor(AppSettingsNotifier)
const appSettingsProvider = AppSettingsNotifierProvider._();
final appSettingsProvider = AppSettingsNotifierProvider._();
final class AppSettingsNotifierProvider
extends $NotifierProvider<AppSettingsNotifier, AppSettings> {
const AppSettingsNotifierProvider._()
AppSettingsNotifierProvider._()
: super(
from: null,
argument: null,
@@ -65,14 +65,13 @@ final class AppSettingsNotifierProvider
}
String _$appSettingsNotifierHash() =>
r'6592261baf8182fe78d3e58e2fd9bb53d3287736';
r'2437c621dcb1625a120ed1f21ab5c29906ba98be';
abstract class _$AppSettingsNotifier extends $Notifier<AppSettings> {
AppSettings build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<AppSettings, AppSettings>;
final element =
ref.element
@@ -82,6 +81,6 @@ abstract class _$AppSettingsNotifier extends $Notifier<AppSettings> {
Object?,
Object?
>;
element.handleValue(ref, created);
element.handleCreate(ref, build);
}
}

View File

@@ -18,7 +18,8 @@ final indexedCloudFileListProvider = AsyncNotifierProvider.autoDispose(
IndexedCloudFileListNotifier.new,
);
class IndexedCloudFileListNotifier extends AsyncNotifier<List<FileListItem>>
class IndexedCloudFileListNotifier
extends AsyncNotifier<PaginationState<FileListItem>>
with AsyncPaginationController<FileListItem> {
String _currentPath = '/';
String? _poolId;
@@ -51,6 +52,19 @@ class IndexedCloudFileListNotifier extends AsyncNotifier<List<FileListItem>>
ref.invalidateSelf();
}
@override
FutureOr<PaginationState<FileListItem>> build() async {
final items = await fetch();
return PaginationState(
items: items,
isLoading: false,
isReloading: false,
totalCount: null,
hasMore: false,
cursor: null,
);
}
@override
Future<List<FileListItem>> fetch() async {
final client = ref.read(apiClientProvider);
@@ -96,7 +110,8 @@ final unindexedFileListProvider = AsyncNotifierProvider.autoDispose(
UnindexedFileListNotifier.new,
);
class UnindexedFileListNotifier extends AsyncNotifier<List<FileListItem>>
class UnindexedFileListNotifier
extends AsyncNotifier<PaginationState<FileListItem>>
with AsyncPaginationController<FileListItem> {
String? _poolId;
bool _recycled = false;
@@ -131,6 +146,19 @@ class UnindexedFileListNotifier extends AsyncNotifier<List<FileListItem>>
static const int pageSize = 20;
@override
FutureOr<PaginationState<FileListItem>> build() async {
final items = await fetch();
return PaginationState(
items: items,
isLoading: false,
isReloading: false,
totalCount: totalCount,
hasMore: hasMore,
cursor: cursor,
);
}
@override
Future<List<FileListItem>> fetch() async {
final client = ref.read(apiClientProvider);

View File

@@ -10,7 +10,7 @@ part of 'file_list.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(billingUsage)
const billingUsageProvider = BillingUsageProvider._();
final billingUsageProvider = BillingUsageProvider._();
final class BillingUsageProvider
extends
@@ -22,7 +22,7 @@ final class BillingUsageProvider
with
$FutureModifier<Map<String, dynamic>?>,
$FutureProvider<Map<String, dynamic>?> {
const BillingUsageProvider._()
BillingUsageProvider._()
: super(
from: null,
argument: null,
@@ -51,7 +51,7 @@ final class BillingUsageProvider
String _$billingUsageHash() => r'58d8bc774868d60781574c85d6b25869a79c57aa';
@ProviderFor(billingQuota)
const billingQuotaProvider = BillingQuotaProvider._();
final billingQuotaProvider = BillingQuotaProvider._();
final class BillingQuotaProvider
extends
@@ -63,7 +63,7 @@ final class BillingQuotaProvider
with
$FutureModifier<Map<String, dynamic>?>,
$FutureProvider<Map<String, dynamic>?> {
const BillingQuotaProvider._()
BillingQuotaProvider._()
: super(
from: null,
argument: null,

View File

@@ -10,7 +10,7 @@ part of 'file_references.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(fileReferences)
const fileReferencesProvider = FileReferencesFamily._();
final fileReferencesProvider = FileReferencesFamily._();
final class FileReferencesProvider
extends
@@ -20,7 +20,7 @@ final class FileReferencesProvider
FutureOr<List<Reference>>
>
with $FutureModifier<List<Reference>>, $FutureProvider<List<Reference>> {
const FileReferencesProvider._({
FileReferencesProvider._({
required FileReferencesFamily super.from,
required String super.argument,
}) : super(
@@ -68,7 +68,7 @@ String _$fileReferencesHash() => r'd66c678c221f61978bdb242b98e6dbe31d0c204b';
final class FileReferencesFamily extends $Family
with $FunctionalFamilyOverride<FutureOr<List<Reference>>, String> {
const FileReferencesFamily._()
FileReferencesFamily._()
: super(
retry: null,
name: r'fileReferencesProvider',

View File

@@ -12,7 +12,7 @@ part of 'event_calendar.dart';
/// This can be used anywhere in the app where calendar data is needed
@ProviderFor(eventCalendar)
const eventCalendarProvider = EventCalendarFamily._();
final eventCalendarProvider = EventCalendarFamily._();
/// Provider for fetching event calendar data
/// This can be used anywhere in the app where calendar data is needed
@@ -29,7 +29,7 @@ final class EventCalendarProvider
$FutureProvider<List<SnEventCalendarEntry>> {
/// Provider for fetching event calendar data
/// This can be used anywhere in the app where calendar data is needed
const EventCalendarProvider._({
EventCalendarProvider._({
required EventCalendarFamily super.from,
required EventCalendarQuery super.argument,
}) : super(
@@ -84,7 +84,7 @@ final class EventCalendarFamily extends $Family
FutureOr<List<SnEventCalendarEntry>>,
EventCalendarQuery
> {
const EventCalendarFamily._()
EventCalendarFamily._()
: super(
retry: null,
name: r'eventCalendarProvider',

View File

@@ -10,11 +10,11 @@ part of 'link_preview.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(LinkPreview)
const linkPreviewProvider = LinkPreviewFamily._();
final linkPreviewProvider = LinkPreviewFamily._();
final class LinkPreviewProvider
extends $AsyncNotifierProvider<LinkPreview, SnScrappedLink?> {
const LinkPreviewProvider._({
LinkPreviewProvider._({
required LinkPreviewFamily super.from,
required String super.argument,
}) : super(
@@ -61,7 +61,7 @@ final class LinkPreviewFamily extends $Family
FutureOr<SnScrappedLink?>,
String
> {
const LinkPreviewFamily._()
LinkPreviewFamily._()
: super(
retry: null,
name: r'linkPreviewProvider',
@@ -85,7 +85,6 @@ abstract class _$LinkPreview extends $AsyncNotifier<SnScrappedLink?> {
@$mustCallSuper
@override
void runBuild() {
final created = build(_$args);
final ref = this.ref as $Ref<AsyncValue<SnScrappedLink?>, SnScrappedLink?>;
final element =
ref.element
@@ -95,6 +94,6 @@ abstract class _$LinkPreview extends $AsyncNotifier<SnScrappedLink?> {
Object?,
Object?
>;
element.handleValue(ref, created);
element.handleCreate(ref, () => build(_$args));
}
}

View File

@@ -10,11 +10,11 @@ part of 'network.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(NetworkStatusNotifier)
const networkStatusProvider = NetworkStatusNotifierProvider._();
final networkStatusProvider = NetworkStatusNotifierProvider._();
final class NetworkStatusNotifierProvider
extends $NotifierProvider<NetworkStatusNotifier, NetworkStatus> {
const NetworkStatusNotifierProvider._()
NetworkStatusNotifierProvider._()
: super(
from: null,
argument: null,
@@ -49,7 +49,6 @@ abstract class _$NetworkStatusNotifier extends $Notifier<NetworkStatus> {
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<NetworkStatus, NetworkStatus>;
final element =
ref.element
@@ -59,6 +58,6 @@ abstract class _$NetworkStatusNotifier extends $Notifier<NetworkStatus> {
Object?,
Object?
>;
element.handleValue(ref, created);
element.handleCreate(ref, build);
}
}

View File

@@ -2,12 +2,49 @@ import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class PaginationState<T> {
final List<T> items;
final bool isLoading;
final bool isReloading;
final int? totalCount;
final bool hasMore;
final String? cursor;
const PaginationState({
required this.items,
required this.isLoading,
required this.isReloading,
required this.totalCount,
required this.hasMore,
required this.cursor,
});
PaginationState<T> copyWith({
List<T>? items,
bool? isLoading,
bool? isReloading,
int? totalCount,
bool? hasMore,
String? cursor,
}) {
return PaginationState<T>(
items: items ?? this.items,
isLoading: isLoading ?? this.isLoading,
isReloading: isReloading ?? this.isReloading,
totalCount: totalCount ?? this.totalCount,
hasMore: hasMore ?? this.hasMore,
cursor: cursor ?? this.cursor,
);
}
}
abstract class PaginationController<T> {
int? get totalCount;
int get fetchedCount;
bool get fetchedAll;
bool get isLoading;
bool get isReloading;
bool get hasMore;
set hasMore(bool value);
String? get cursor;
@@ -26,46 +63,86 @@ abstract class PaginationFiltered<F> {
Future<void> applyFilter(F filter);
}
mixin AsyncPaginationController<T> on AsyncNotifier<List<T>>
mixin AsyncPaginationController<T> on AsyncNotifier<PaginationState<T>>
implements PaginationController<T> {
@override
int? totalCount;
@override
int get fetchedCount => state.value?.length ?? 0;
int get fetchedCount =>
state.value?.isReloading == true ? 0 : state.value?.items.length ?? 0;
@override
bool get fetchedAll =>
!hasMore || (totalCount != null && fetchedCount >= totalCount!);
!(state.value?.hasMore ?? true) ||
((state.value?.totalCount != null &&
fetchedCount >= state.value!.totalCount!));
@override
bool isLoading = false;
bool get isLoading => state.value?.isLoading ?? false;
@override
bool hasMore = true;
bool get isReloading => state.value?.isReloading ?? false;
@override
String? cursor;
bool get hasMore => state.value?.hasMore ?? true;
@override
FutureOr<List<T>> build() async {
cursor = null;
return fetch();
String? get cursor => state.value?.cursor;
@override
set hasMore(bool value) {
if (state is AsyncData) {
state = AsyncData((state as AsyncData).value.copyWith(hasMore: value));
}
}
@override
set cursor(String? value) {
if (state is AsyncData) {
state = AsyncData((state as AsyncData).value.copyWith(cursor: value));
}
}
@override
FutureOr<PaginationState<T>> build() async {
final items = await fetch();
return PaginationState(
items: items,
isLoading: false,
isReloading: false,
totalCount: totalCount,
hasMore: hasMore,
cursor: cursor,
);
}
@override
Future<void> refresh() async {
isLoading = true;
totalCount = null;
hasMore = true;
cursor = null;
state = AsyncLoading<List<T>>();
state = AsyncData(
PaginationState(
items: [],
isLoading: true,
isReloading: true,
totalCount: null,
hasMore: true,
cursor: null,
),
);
final newState = await AsyncValue.guard<List<T>>(() async {
return await fetch();
});
state = newState;
isLoading = false;
final newItems = await fetch();
if (!ref.mounted) return;
state = AsyncData(
PaginationState(
items: newItems,
isLoading: false,
isReloading: false,
totalCount: totalCount,
hasMore: hasMore,
cursor: cursor,
),
);
}
@override
@@ -73,16 +150,17 @@ mixin AsyncPaginationController<T> on AsyncNotifier<List<T>>
if (fetchedAll) return;
if (isLoading) return;
isLoading = true;
state = AsyncLoading<List<T>>();
state = AsyncData(state.value!.copyWith(isLoading: true));
final newState = await AsyncValue.guard<List<T>>(() async {
final elements = await fetch();
return [...?state.value, ...elements];
});
final newItems = await fetch();
state = newState;
isLoading = false;
if (!ref.mounted) return;
state = AsyncData(
state.value!.copyWith(
items: [...state.value!.items, ...newItems],
isLoading: false,
),
);
}
}
@@ -91,18 +169,31 @@ mixin AsyncPaginationFilter<F, T> on AsyncPaginationController<T>
@override
Future<void> applyFilter(F filter) async {
if (currentFilter == filter) return;
// Reset the data
isLoading = true;
totalCount = null;
hasMore = true;
cursor = null;
state = AsyncLoading<List<T>>();
state = AsyncData(
PaginationState(
items: [],
isLoading: true,
isReloading: true,
totalCount: null,
hasMore: true,
cursor: null,
),
);
currentFilter = filter;
final newState = await AsyncValue.guard<List<T>>(() async {
return await fetch();
});
state = newState;
isLoading = false;
final newItems = await fetch();
if (!ref.mounted) return;
state = AsyncData(
PaginationState(
items: newItems,
isLoading: false,
isReloading: false,
totalCount: totalCount,
hasMore: hasMore,
cursor: cursor,
),
);
}
}

View File

@@ -1,4 +1,6 @@
// Post Categories Notifier
import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/post_category.dart';
import 'package:island/models/post_tag.dart';
@@ -8,11 +10,25 @@ import 'package:island/pods/paging.dart';
final postCategoriesProvider =
AsyncNotifierProvider.autoDispose<
PostCategoriesNotifier,
List<SnPostCategory>
PaginationState<SnPostCategory>
>(PostCategoriesNotifier.new);
class PostCategoriesNotifier extends AsyncNotifier<List<SnPostCategory>>
class PostCategoriesNotifier
extends AsyncNotifier<PaginationState<SnPostCategory>>
with AsyncPaginationController<SnPostCategory> {
@override
FutureOr<PaginationState<SnPostCategory>> build() async {
final items = await fetch();
return PaginationState(
items: items,
isLoading: false,
isReloading: false,
totalCount: totalCount,
hasMore: hasMore,
cursor: cursor,
);
}
@override
Future<List<SnPostCategory>> fetch() async {
final client = ref.read(apiClientProvider);
@@ -30,12 +46,26 @@ class PostCategoriesNotifier extends AsyncNotifier<List<SnPostCategory>>
// Post Tags Notifier
final postTagsProvider =
AsyncNotifierProvider.autoDispose<PostTagsNotifier, List<SnPostTag>>(
PostTagsNotifier.new,
);
AsyncNotifierProvider.autoDispose<
PostTagsNotifier,
PaginationState<SnPostTag>
>(PostTagsNotifier.new);
class PostTagsNotifier extends AsyncNotifier<List<SnPostTag>>
class PostTagsNotifier extends AsyncNotifier<PaginationState<SnPostTag>>
with AsyncPaginationController<SnPostTag> {
@override
FutureOr<PaginationState<SnPostTag>> build() async {
final items = await fetch();
return PaginationState(
items: items,
isLoading: false,
isReloading: false,
totalCount: totalCount,
hasMore: hasMore,
cursor: cursor,
);
}
@override
Future<List<SnPostTag>> fetch() async {
final client = ref.read(apiClientProvider);

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/post.dart';
@@ -39,7 +41,7 @@ final postListProvider = AsyncNotifierProvider.autoDispose.family(
PostListNotifier.new,
);
class PostListNotifier extends AsyncNotifier<List<SnPost>>
class PostListNotifier extends AsyncNotifier<PaginationState<SnPost>>
with
AsyncPaginationController<SnPost>,
AsyncPaginationFilter<PostListQuery, SnPost> {
@@ -53,9 +55,17 @@ class PostListNotifier extends AsyncNotifier<List<SnPost>>
late PostListQuery currentFilter;
@override
Future<List<SnPost>> build() async {
FutureOr<PaginationState<SnPost>> build() async {
currentFilter = config.initialFilter;
return fetch();
final items = await fetch();
return PaginationState(
items: items,
isLoading: false,
isReloading: false,
totalCount: totalCount,
hasMore: hasMore,
cursor: cursor,
);
}
@override

View File

@@ -10,7 +10,7 @@ part of 'site_files.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(siteFiles)
const siteFilesProvider = SiteFilesFamily._();
final siteFilesProvider = SiteFilesFamily._();
final class SiteFilesProvider
extends
@@ -22,7 +22,7 @@ final class SiteFilesProvider
with
$FutureModifier<List<SnSiteFileEntry>>,
$FutureProvider<List<SnSiteFileEntry>> {
const SiteFilesProvider._({
SiteFilesProvider._({
required SiteFilesFamily super.from,
required ({String siteId, String? path}) super.argument,
}) : super(
@@ -74,7 +74,7 @@ final class SiteFilesFamily extends $Family
FutureOr<List<SnSiteFileEntry>>,
({String siteId, String? path})
> {
const SiteFilesFamily._()
SiteFilesFamily._()
: super(
retry: null,
name: r'siteFilesProvider',
@@ -91,7 +91,7 @@ final class SiteFilesFamily extends $Family
}
@ProviderFor(siteFileContent)
const siteFileContentProvider = SiteFileContentFamily._();
final siteFileContentProvider = SiteFileContentFamily._();
final class SiteFileContentProvider
extends
@@ -101,7 +101,7 @@ final class SiteFileContentProvider
FutureOr<SnFileContent>
>
with $FutureModifier<SnFileContent>, $FutureProvider<SnFileContent> {
const SiteFileContentProvider._({
SiteFileContentProvider._({
required SiteFileContentFamily super.from,
required ({String siteId, String relativePath}) super.argument,
}) : super(
@@ -157,7 +157,7 @@ final class SiteFileContentFamily extends $Family
FutureOr<SnFileContent>,
({String siteId, String relativePath})
> {
const SiteFileContentFamily._()
SiteFileContentFamily._()
: super(
retry: null,
name: r'siteFileContentProvider',
@@ -179,12 +179,12 @@ final class SiteFileContentFamily extends $Family
}
@ProviderFor(siteFileContentRaw)
const siteFileContentRawProvider = SiteFileContentRawFamily._();
final siteFileContentRawProvider = SiteFileContentRawFamily._();
final class SiteFileContentRawProvider
extends $FunctionalProvider<AsyncValue<String>, String, FutureOr<String>>
with $FutureModifier<String>, $FutureProvider<String> {
const SiteFileContentRawProvider._({
SiteFileContentRawProvider._({
required SiteFileContentRawFamily super.from,
required ({String siteId, String relativePath}) super.argument,
}) : super(
@@ -240,7 +240,7 @@ final class SiteFileContentRawFamily extends $Family
FutureOr<String>,
({String siteId, String relativePath})
> {
const SiteFileContentRawFamily._()
SiteFileContentRawFamily._()
: super(
retry: null,
name: r'siteFileContentRawProvider',

View File

@@ -10,7 +10,7 @@ part of 'site_pages.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(sitePages)
const sitePagesProvider = SitePagesFamily._();
final sitePagesProvider = SitePagesFamily._();
final class SitePagesProvider
extends
@@ -22,7 +22,7 @@ final class SitePagesProvider
with
$FutureModifier<List<SnPublicationPage>>,
$FutureProvider<List<SnPublicationPage>> {
const SitePagesProvider._({
SitePagesProvider._({
required SitePagesFamily super.from,
required (String, String) super.argument,
}) : super(
@@ -74,7 +74,7 @@ final class SitePagesFamily extends $Family
FutureOr<List<SnPublicationPage>>,
(String, String)
> {
const SitePagesFamily._()
SitePagesFamily._()
: super(
retry: null,
name: r'sitePagesProvider',
@@ -91,7 +91,7 @@ final class SitePagesFamily extends $Family
}
@ProviderFor(sitePage)
const sitePageProvider = SitePageFamily._();
final sitePageProvider = SitePageFamily._();
final class SitePageProvider
extends
@@ -103,7 +103,7 @@ final class SitePageProvider
with
$FutureModifier<SnPublicationPage>,
$FutureProvider<SnPublicationPage> {
const SitePageProvider._({
SitePageProvider._({
required SitePageFamily super.from,
required String super.argument,
}) : super(
@@ -151,7 +151,7 @@ String _$sitePageHash() => r'542f70c5b103fe34d7cf7eb0821d52f017022efc';
final class SitePageFamily extends $Family
with $FunctionalFamilyOverride<FutureOr<SnPublicationPage>, String> {
const SitePageFamily._()
SitePageFamily._()
: super(
retry: null,
name: r'sitePageProvider',

View File

@@ -10,12 +10,12 @@ part of 'theme.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(theme)
const themeProvider = ThemeProvider._();
final themeProvider = ThemeProvider._();
final class ThemeProvider
extends $FunctionalProvider<ThemeSet, ThemeSet, ThemeSet>
with $Provider<ThemeSet> {
const ThemeProvider._()
ThemeProvider._()
: super(
from: null,
argument: null,

View File

@@ -1,5 +1,8 @@
import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/activity.dart';
import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart';
import 'package:island/pods/paging.dart';
@@ -7,23 +10,39 @@ final activityListProvider = AsyncNotifierProvider.autoDispose(
ActivityListNotifier.new,
);
class ActivityListNotifier extends AsyncNotifier<List<SnTimelineEvent>>
class ActivityListNotifier
extends AsyncNotifier<PaginationState<SnTimelineEvent>>
with
AsyncPaginationController<SnTimelineEvent>,
AsyncPaginationFilter<String?, SnTimelineEvent> {
static const int pageSize = 20;
@override
FutureOr<PaginationState<SnTimelineEvent>> build() async {
final items = await fetch();
return PaginationState(
items: items,
isLoading: false,
isReloading: false,
totalCount: totalCount,
hasMore: hasMore,
cursor: cursor,
);
}
@override
String? currentFilter;
@override
Future<List<SnTimelineEvent>> fetch() async {
final client = ref.read(apiClientProvider);
final settings = ref.read(appSettingsProvider);
final queryParameters = {
if (cursor != null) 'cursor': cursor,
'take': pageSize,
if (currentFilter != null) 'filter': currentFilter,
'showFediverse': settings.showFediverseContent,
};
final response = await client.get(
@@ -39,11 +58,18 @@ class ActivityListNotifier extends AsyncNotifier<List<SnTimelineEvent>>
// Find the latest createdAt timestamp from all items for cursor-based pagination
// This ensures we get items created before this timestamp, regardless of sort order
if (items.isNotEmpty) {
final latestCreatedAt = items
final newestCreatedAt = items
.where((e) => e.type.startsWith('posts.'))
.map((e) => e.createdAt)
.reduce((a, b) => a.isBefore(b) ? a : b);
cursor = latestCreatedAt.toUtc().toIso8601String();
if (cursor != null) {
final prevCursor = DateTime.tryParse(cursor!);
if (prevCursor != null && prevCursor.isAfter(newestCreatedAt)) {
cursor = newestCreatedAt.toUtc().toIso8601String();
}
} else {
cursor = newestCreatedAt.toUtc().toIso8601String();
}
}
return items;
@@ -53,9 +79,9 @@ class ActivityListNotifier extends AsyncNotifier<List<SnTimelineEvent>>
final currentState = state.value;
if (currentState == null) return;
final updatedItems = [...currentState];
final updatedItems = [...currentState.items];
updatedItems[index] = activity;
state = AsyncData(updatedItems);
state = AsyncData(currentState.copyWith(items: updatedItems));
}
}

View File

@@ -10,12 +10,12 @@ part of 'translate.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(translateString)
const translateStringProvider = TranslateStringFamily._();
final translateStringProvider = TranslateStringFamily._();
final class TranslateStringProvider
extends $FunctionalProvider<AsyncValue<String>, String, FutureOr<String>>
with $FutureModifier<String>, $FutureProvider<String> {
const TranslateStringProvider._({
TranslateStringProvider._({
required TranslateStringFamily super.from,
required TranslateQuery super.argument,
}) : super(
@@ -62,7 +62,7 @@ String _$translateStringHash() => r'51d638cf07cbf3ffa9469298f5bd9c667bc0ccb7';
final class TranslateStringFamily extends $Family
with $FunctionalFamilyOverride<FutureOr<String>, TranslateQuery> {
const TranslateStringFamily._()
TranslateStringFamily._()
: super(
retry: null,
name: r'translateStringProvider',
@@ -79,12 +79,12 @@ final class TranslateStringFamily extends $Family
}
@ProviderFor(detectStringLanguage)
const detectStringLanguageProvider = DetectStringLanguageFamily._();
final detectStringLanguageProvider = DetectStringLanguageFamily._();
final class DetectStringLanguageProvider
extends $FunctionalProvider<String?, String?, String?>
with $Provider<String?> {
const DetectStringLanguageProvider._({
DetectStringLanguageProvider._({
required DetectStringLanguageFamily super.from,
required String super.argument,
}) : super(
@@ -140,7 +140,7 @@ String _$detectStringLanguageHash() =>
final class DetectStringLanguageFamily extends $Family
with $FunctionalFamilyOverride<String?, String> {
const DetectStringLanguageFamily._()
DetectStringLanguageFamily._()
: super(
retry: null,
name: r'detectStringLanguageProvider',

View File

@@ -8,7 +8,7 @@ import 'package:island/pods/network.dart';
final webFeedListProvider = FutureProvider.autoDispose
.family<List<SnWebFeed>, String>((ref, pubName) async {
final client = ref.watch(apiClientProvider);
final response = await client.get('/sphere/publishers/$pubName/feeds');
final response = await client.get('/insight/publishers/$pubName/feeds');
return (response.data as List)
.map((json) => SnWebFeed.fromJson(json))
.toList();

View File

@@ -18,7 +18,7 @@ import 'package:island/screens/files/file_list.dart';
import 'package:island/screens/files/file_detail.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_search.dart';
import 'package:island/screens/search.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/app_wrapper.dart';
import 'package:island/screens/tabs.dart';
@@ -169,6 +169,7 @@ final routerProvider = Provider<GoRouter>((ref) {
builder: (context, state) => const AboutScreen(),
),
// File routes
GoRoute(
name: 'fileDetail',
path: '/files/:id',
@@ -185,6 +186,56 @@ final routerProvider = Provider<GoRouter>((ref) {
},
),
// Post routes
GoRoute(
name: 'postShuffle',
path: '/posts/shuffle',
builder: (context, state) => const PostShuffleScreen(),
),
GoRoute(
name: 'postCategories',
path: '/posts/categories',
builder: (context, state) => const PostCategoriesListScreen(),
),
GoRoute(
name: 'postCategoryDetail',
path: '/posts/categories/:slug',
builder: (context, state) {
final slug = state.pathParameters['slug']!;
return PostCategoryDetailScreen(slug: slug, isCategory: true);
},
),
GoRoute(
name: 'postTagDetail',
path: '/posts/tags/:slug',
builder: (context, state) {
final slug = state.pathParameters['slug']!;
return PostCategoryDetailScreen(slug: slug, isCategory: false);
},
),
GoRoute(
name: 'postDetail',
path: '/posts/:id',
builder: (context, state) {
final id = state.pathParameters['id']!;
return PostDetailScreen(id: id);
},
),
GoRoute(
name: 'publisherProfile',
path: '/publishers/:name',
builder: (context, state) {
final name = state.pathParameters['name']!;
return PublisherProfileScreen(name: name);
},
),
GoRoute(
name: 'universalSearch',
path: '/search',
builder: (context, state) => const UniversalSearchScreen(),
),
// Main tabs with TabsScreen shell
ShellRoute(
navigatorKey: _tabsShellKey,
@@ -212,56 +263,7 @@ final routerProvider = Provider<GoRouter>((ref) {
transitionsBuilder: _tabPagesTransitionBuilder,
),
),
GoRoute(
name: 'postSearch',
path: '/posts/search',
builder: (context, state) => const PostSearchScreen(),
),
GoRoute(
name: 'postShuffle',
path: '/posts/shuffle',
builder: (context, state) => const PostShuffleScreen(),
),
GoRoute(
name: 'postCategories',
path: '/posts/categories',
builder: (context, state) => const PostCategoriesListScreen(),
),
GoRoute(
name: 'postCategoryDetail',
path: '/posts/categories/:slug',
builder: (context, state) {
final slug = state.pathParameters['slug']!;
return PostCategoryDetailScreen(slug: slug, isCategory: true);
},
),
GoRoute(
name: 'postTagDetail',
path: '/posts/tags/:slug',
builder: (context, state) {
final slug = state.pathParameters['slug']!;
return PostCategoryDetailScreen(
slug: slug,
isCategory: false,
);
},
),
GoRoute(
name: 'postDetail',
path: '/posts/:id',
builder: (context, state) {
final id = state.pathParameters['id']!;
return PostDetailScreen(id: id);
},
),
GoRoute(
name: 'publisherProfile',
path: '/publishers/:name',
builder: (context, state) {
final name = state.pathParameters['name']!;
return PublisherProfileScreen(name: name);
},
),
GoRoute(
name: 'discoveryRealms',
path: '/discovery/realms',

View File

@@ -74,7 +74,7 @@ class AccountScreen extends HookConsumerWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (user.value?.profile.background?.id != null)
if (user.value?.profile.background != null)
Stack(
clipBehavior: Clip.none,
children: [
@@ -112,7 +112,7 @@ class AccountScreen extends HookConsumerWidget {
Builder(
builder: (context) {
final hasBackground =
user.value?.profile.background?.id != null;
user.value?.profile.background != null;
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
spacing: hasBackground ? 0 : 16,

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
@@ -28,10 +30,23 @@ final socialCreditHistoryNotifierProvider = AsyncNotifierProvider.autoDispose(
);
class SocialCreditHistoryNotifier
extends AsyncNotifier<List<SnSocialCreditRecord>>
extends AsyncNotifier<PaginationState<SnSocialCreditRecord>>
with AsyncPaginationController<SnSocialCreditRecord> {
static const int pageSize = 20;
@override
FutureOr<PaginationState<SnSocialCreditRecord>> build() async {
final items = await fetch();
return PaginationState(
items: items,
isLoading: false,
isReloading: false,
totalCount: totalCount,
hasMore: hasMore,
cursor: cursor,
);
}
@override
Future<List<SnSocialCreditRecord>> fetch() async {
final client = ref.read(apiClientProvider);

View File

@@ -10,12 +10,12 @@ part of 'credits.dart';
// ignore_for_file: type=lint, type=warning
@ProviderFor(socialCredits)
const socialCreditsProvider = SocialCreditsProvider._();
final socialCreditsProvider = SocialCreditsProvider._();
final class SocialCreditsProvider
extends $FunctionalProvider<AsyncValue<double>, double, FutureOr<double>>
with $FutureModifier<double>, $FutureProvider<double> {
const SocialCreditsProvider._()
SocialCreditsProvider._()
: super(
from: null,
argument: null,

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -14,14 +16,30 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:island/widgets/paging/pagination_list.dart';
import 'package:styled_widget/styled_widget.dart';
final levelingHistoryNotifierProvider = AsyncNotifierProvider.autoDispose(
LevelingHistoryNotifier.new,
);
final levelingHistoryNotifierProvider =
AsyncNotifierProvider.autoDispose<
LevelingHistoryNotifier,
PaginationState<SnExperienceRecord>
>(LevelingHistoryNotifier.new);
class LevelingHistoryNotifier extends AsyncNotifier<List<SnExperienceRecord>>
class LevelingHistoryNotifier
extends AsyncNotifier<PaginationState<SnExperienceRecord>>
with AsyncPaginationController<SnExperienceRecord> {
static const int pageSize = 20;
@override
FutureOr<PaginationState<SnExperienceRecord>> build() async {
final items = await fetch();
return PaginationState(
items: items,
isLoading: false,
isReloading: false,
totalCount: totalCount,
hasMore: hasMore,
cursor: cursor,
);
}
@override
Future<List<SnExperienceRecord>> fetch() async {
final client = ref.read(apiClientProvider);

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