Compare commits

..

95 Commits

Author SHA1 Message Date
LittleSheep
6558854a7a 🚀 Launch 3.1.0+120 2025-08-09 13:37:35 +08:00
LittleSheep
892035ab27 🐛 Somehow fix production video player issue 2025-08-09 13:35:54 +08:00
LittleSheep
87ae8d2ff4 🚀 Launch 3.1.0+119 2025-08-09 01:44:18 +08:00
LittleSheep
15c2dbaa0d 🐛 Fix developer 2025-08-09 01:41:51 +08:00
LittleSheep
6b3338b885 🚀 Launch 3.1.0+118 2025-08-09 00:48:50 +08:00
LittleSheep
bb00b1bc6a Debug options 2025-08-09 00:44:37 +08:00
LittleSheep
5e1a15ada2 Show profile links 2025-08-09 00:27:43 +08:00
LittleSheep
9bdf8ba346 🐛 Fix bugs 2025-08-09 00:21:27 +08:00
LittleSheep
204c087f29 Edit profile links 2025-08-09 00:15:35 +08:00
LittleSheep
1def3e1895 💄 Optimize profile page 2025-08-08 23:46:40 +08:00
LittleSheep
550c74e544 🐛 Fix unable to subscribe 2025-08-08 23:23:16 +08:00
LittleSheep
a39565f012 Post category details 2025-08-08 23:20:22 +08:00
LittleSheep
aa9755e6a7 💄 Optimized post category and tag 2025-08-08 22:19:17 +08:00
LittleSheep
b25e8d661a Show post tags and categories 2025-08-08 21:18:25 +08:00
LittleSheep
4b253ac3ec ⚗️ Testing out the firebase options on linux 2025-08-08 19:47:51 +08:00
LittleSheep
5d1b875d3c 🐛 Disable firebase on linux to prevent errors 2025-08-08 19:41:48 +08:00
LittleSheep
e2e103fa67 Post categories selection 2025-08-08 17:57:47 +08:00
LittleSheep
43c90da4e3 🐛 Fixes attachments 2025-08-08 15:04:50 +08:00
LittleSheep
fa210dd98f 💄 Post featured optimization 2025-08-08 14:44:59 +08:00
LittleSheep
43d9ca92bf Featured post 2025-08-08 03:28:16 +08:00
LittleSheep
5e592c143f ♻️ Rebuild the dupe device 2025-08-08 03:16:16 +08:00
LittleSheep
0c59816f26 👽 Update developer's path 2025-08-08 03:11:38 +08:00
LittleSheep
19c2457895 Network status, poll submit feedback 2025-08-08 02:56:44 +08:00
LittleSheep
af8d87857e Update service 2025-08-07 14:29:20 +08:00
LittleSheep
d05f63a36a 🐛 Bug fixes 2025-08-07 13:29:33 +08:00
LittleSheep
e2dc520012 Post item show more info 2025-08-07 12:17:37 +08:00
LittleSheep
cff9c15e31 🐛 Slicence post list disposed 2025-08-07 12:07:33 +08:00
LittleSheep
f00135c4bf 🐛 Fix room input 2025-08-07 12:04:47 +08:00
LittleSheep
30b8a6c30f 🐛 Fix status overflow 2025-08-07 11:56:37 +08:00
LittleSheep
b9c4ee31b1 Adjust picker size 2025-08-07 11:55:16 +08:00
LittleSheep
87870af866 Sticker picker basis 2025-08-07 03:22:56 +08:00
LittleSheep
b83cb0fb0b ♻️ Replace the snackbar usage 2025-08-07 03:03:15 +08:00
LittleSheep
7fd1fe34e5 Stickers marketplace 2025-08-07 03:00:29 +08:00
LittleSheep
1c18330891 🐛 Fixes poll feedback 2025-08-07 02:17:03 +08:00
LittleSheep
d320879ad0 Poll feedback 2025-08-07 02:01:15 +08:00
LittleSheep
950150e119 🐛 Fix large screen have no post category filter 2025-08-06 21:42:23 +08:00
LittleSheep
3c4a9767e1 ♻️ Move the title, description out of the settings sheet 2025-08-06 20:55:13 +08:00
LittleSheep
5df2445f3f 📝 Update user agreement 2025-08-06 20:30:59 +08:00
LittleSheep
56543d7b4c Publisher page category filter 2025-08-06 20:17:07 +08:00
LittleSheep
4c6fea1242 💄 Optimize cloud video 2025-08-06 18:49:04 +08:00
LittleSheep
fff43de9e3 🐛 Dozens of bug fixes 2025-08-06 18:15:13 +08:00
LittleSheep
b31a915544 🐛 Bug fixes 2025-08-06 14:57:42 +08:00
LittleSheep
8956723ac5 🚀 Launch 3.1.0+117 2025-08-06 03:30:33 +08:00
LittleSheep
ccc3ac415e 🐛 Poll bug fixes 2025-08-06 03:14:09 +08:00
LittleSheep
8c47a59b80 🐛 Bug fixes 2025-08-06 02:51:41 +08:00
LittleSheep
a6d869ebf6 Poll answer 2025-08-06 01:37:38 +08:00
LittleSheep
f3a8699389 🐛 Fixes poll editor 2025-08-05 18:09:34 +08:00
LittleSheep
d345c00e84 Poll editor basic 2025-08-05 17:40:47 +08:00
LittleSheep
a706f127b6 💄 Optimize hint 2025-08-05 02:59:08 +08:00
LittleSheep
680ece0b6a Sensitive marks hidden image 2025-08-05 02:57:27 +08:00
LittleSheep
b976c6ed37 File rename, sensitive 2025-08-05 00:53:00 +08:00
LittleSheep
6ae6b132de Use Solar Network drive hint 2025-08-04 22:37:20 +08:00
LittleSheep
95aec7c95b 💄 Change icon 2025-08-04 22:15:42 +08:00
LittleSheep
edd760fbcb Ability to crop image 2025-08-04 22:08:18 +08:00
LittleSheep
ba269dbbb8 💄 Optimization styles 2025-08-04 18:20:13 +08:00
LittleSheep
1aa45dd9f1 🐛 Fix bottom sheet didn't avoid keyboard 2025-08-04 17:36:04 +08:00
LittleSheep
92685d7410 💄 Optimize profile page 2025-08-04 17:24:17 +08:00
LittleSheep
c8e351514d 🐛 Dozens of bug fixes 2025-08-04 13:34:50 +08:00
LittleSheep
f3900825e3 🐛 Fix oidc 2025-08-03 12:34:12 +08:00
LittleSheep
2cc6652f75 Add audio from existing file 2025-08-02 15:35:18 +08:00
LittleSheep
4d4409de2e Show audio filename 2025-08-02 15:13:50 +08:00
LittleSheep
e1286c797f Audio player 2025-08-02 14:54:56 +08:00
LittleSheep
bec037622f Audio recorder 2025-08-02 14:06:58 +08:00
LittleSheep
a0d8c1a9b3 🐛 Fixes call 2025-08-02 01:53:02 +08:00
LittleSheep
26135d2116 🐛 Fix embed 2025-08-01 23:54:31 +08:00
LittleSheep
71b67fd22d 🐛 Fixes of videos and more 2025-08-01 23:21:43 +08:00
LittleSheep
855072dfea 🐛 Fix refresh failed after delete data 2025-08-01 21:46:43 +08:00
LittleSheep
b39e2e2d64 💄 Video player optimized 2025-08-01 20:36:39 +08:00
LittleSheep
84b1d6a346 💄 Optimization 2025-08-01 18:03:54 +08:00
LittleSheep
28335dd548 💄 Optimize profile page 2025-08-01 16:29:23 +08:00
LittleSheep
7253e2d3ef 🐛 Fix fast scroll explore cause error 2025-08-01 13:03:05 +08:00
LittleSheep
4d489425fa Comment threading
👽 Fix chat notify level
2025-08-01 13:01:38 +08:00
LittleSheep
890a8a44cf Adjustable zoom in image quaility 2025-08-01 11:37:56 +08:00
LittleSheep
8e3583f57a 💄 Optimize post 2025-08-01 11:30:38 +08:00
LittleSheep
d0ff14659f 🚀 Launch 3.1.0+116 2025-08-01 01:36:31 +08:00
LittleSheep
1f7caaeaac 💄 Optimize call 2025-08-01 01:03:01 +08:00
LittleSheep
9f9f42071a 💄 Fixes of call and optimizations 2025-08-01 00:12:18 +08:00
LittleSheep
6bd6e994cb Refreshed article post editor 2025-07-31 22:39:27 +08:00
LittleSheep
02e68d76ee 💄 Optimized creator mode post item 2025-07-31 22:31:22 +08:00
LittleSheep
d04b06089c 💄 Optimized reaction sheet 2025-07-31 22:27:25 +08:00
LittleSheep
9be6fea2e0 ⬆️ Upgrade deps in order to fix firebase breaking changes 2025-07-31 22:04:19 +08:00
LittleSheep
6b1214a06f 🐛 Fix the goddamn AI code 2025-07-31 21:44:14 +08:00
LittleSheep
4597373ac9 Message translate 2025-07-31 21:16:38 +08:00
LittleSheep
047c8d93aa Translate infra and post translate 2025-07-31 21:05:29 +08:00
LittleSheep
715f95ca22 🐛 Bug fixes 2025-07-31 16:48:50 +08:00
LittleSheep
ba709012d7 💄 Optimize article compose 2025-07-31 02:32:03 +08:00
LittleSheep
fd186f8391 💄 Optimize post compose and more 2025-07-31 02:01:18 +08:00
LittleSheep
262d36cd2d 🐛 Fix bugs 2025-07-31 01:20:11 +08:00
LittleSheep
f320855348 💄 Optimize explore page styles 2025-07-31 01:17:20 +08:00
LittleSheep
ed90152462 🐛 Fix message with link will not able to load 2025-07-30 23:04:59 +08:00
LittleSheep
6e5c5f1690 💄 Optimized post item 2025-07-30 22:36:27 +08:00
LittleSheep
7c92dee097 Edited the post item styles 2025-07-30 22:16:36 +08:00
LittleSheep
e4bb031138 💄 Optimize file viewer 2025-07-30 00:29:26 +08:00
LittleSheep
97226ae96b Websocket hearbeat 2025-07-29 18:13:26 +08:00
LittleSheep
d8cd33e79a Add file from ID
🐛 Fix explore unauthorized render error on large screen
2025-07-28 02:01:36 +08:00
168 changed files with 14985 additions and 4185 deletions

View File

@@ -59,7 +59,6 @@ dependencies {
implementation("com.google.android.material:material:1.12.0") implementation("com.google.android.material:material:1.12.0")
implementation("com.github.bumptech.glide:glide:4.16.0") implementation("com.github.bumptech.glide:glide:4.16.0")
implementation("com.squareup.okhttp3:okhttp:4.12.0") implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.google.firebase:firebase-messaging-ktx")
} }
flutter { flutter {

View File

@@ -12,7 +12,12 @@
"package_name": "dev.solsynth.solian" "package_name": "dev.solsynth.solian"
} }
}, },
"oauth_client": [], "oauth_client": [
{
"client_id": "961776991058-963m1qin2vtp8fv693b5fdrab5hmpl89.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [ "api_key": [
{ {
"current_key": "AIzaSyDvFNudXYs29uDtcCv6pFR8h5tXBs90FYk" "current_key": "AIzaSyDvFNudXYs29uDtcCv6pFR8h5tXBs90FYk"
@@ -20,7 +25,20 @@
], ],
"services": { "services": {
"appinvite_service": { "appinvite_service": {
"other_platform_oauth_client": [] "other_platform_oauth_client": [
{
"client_id": "961776991058-963m1qin2vtp8fv693b5fdrab5hmpl89.apps.googleusercontent.com",
"client_type": 3
},
{
"client_id": "961776991058-stt7et4qvn3cpscl4r61gl1hnlatqkig.apps.googleusercontent.com",
"client_type": 2,
"ios_info": {
"bundle_id": "dev.solsynth.solian",
"app_store_id": "6499032345"
}
}
]
} }
} }
} }

View File

@@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
@@ -89,6 +90,13 @@
</intent-filter> </intent-filter>
</activity> </activity>
<!-- Livekit Screenshare -->
<service
android:name="de.julianassmann.flutter_background.IsolateHolderService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="mediaProjection" />
<!-- Sign in with Apple --> <!-- Sign in with Apple -->
<activity <activity
android:name="com.aboutyou.dart_packages.sign_in_with_apple.SignInWithAppleCallback" android:name="com.aboutyou.dart_packages.sign_in_with_apple.SignInWithAppleCallback"
@@ -109,14 +117,6 @@
android:enabled="true" android:enabled="true"
android:exported="true" /> android:exported="true" />
<service
android:name=".service.MessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="dev.solsynth.solian.provider" android:authorities="dev.solsynth.solian.provider"

View File

@@ -1,102 +0,0 @@
package dev.solsynth.solian.service
import android.app.PendingIntent
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.RemoteInput
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import dev.solsynth.solian.MainActivity
import dev.solsynth.solian.receiver.ReplyReceiver
import org.json.JSONObject
class MessagingService: FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
val type = remoteMessage.data["type"]
if (type == "messages.new") {
handleMessageNotification(remoteMessage)
} else {
// Handle other notification types
}
}
private fun handleMessageNotification(remoteMessage: RemoteMessage) {
val data = remoteMessage.data
val metaString = data["meta"] ?: return
val meta = JSONObject(metaString)
val pfp = meta.optString("pfp", null)
val roomId = meta.optString("room_id", null)
val messageId = meta.optString("message_id", null)
val notificationId = System.currentTimeMillis().toInt()
val replyLabel = "Reply"
val remoteInput = RemoteInput.Builder("key_text_reply")
.setLabel(replyLabel)
.build()
val replyIntent = Intent(this, ReplyReceiver::class.java).apply {
putExtra("room_id", roomId)
putExtra("message_id", messageId)
putExtra("notification_id", notificationId)
}
val pendingIntentFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
val replyPendingIntent = PendingIntent.getBroadcast(
applicationContext,
notificationId,
replyIntent,
pendingIntentFlags
)
val action = NotificationCompat.Action.Builder(
android.R.drawable.ic_menu_send,
replyLabel,
replyPendingIntent
)
.addRemoteInput(remoteInput)
.build()
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.putExtra("room_id", roomId)
val pendingIntent = PendingIntent.getActivity(this, 0, intent, pendingIntentFlags)
val notificationBuilder = NotificationCompat.Builder(this, "messages")
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle(remoteMessage.notification?.title)
.setContentText(remoteMessage.notification?.body)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
.addAction(action)
if (pfp != null) {
Glide.with(applicationContext)
.asBitmap()
.load(pfp)
.into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
notificationBuilder.setLargeIcon(resource)
NotificationManagerCompat.from(applicationContext).notify(notificationId, notificationBuilder.build())
}
override fun onLoadCleared(placeholder: Drawable?) {}
})
} else {
NotificationManagerCompat.from(this).notify(notificationId, notificationBuilder.build())
}
}
}

View File

@@ -144,9 +144,16 @@
"other": "{} attachments" "other": "{} attachments"
}, },
"edited": "Edited", "edited": "Edited",
"editedAt": "Edited at {}",
"addVideo": "Add video", "addVideo": "Add video",
"addPhoto": "Add photo", "addPhoto": "Add photo",
"addAudio": "Add audio",
"addFile": "Add file", "addFile": "Add file",
"recordAudio": "Record Audio",
"linkAttachment": "Link Attachment",
"fileIdCannotBeEmpty": "File ID cannot be empty",
"fileIdLinkHint": "Haven't upload to the Solar Network? Tap here to open Solar Network Drive to customize your uploads.",
"failedToFetchFile": "Failed to fetch file: {}",
"createDirectMessage": "Send new DM", "createDirectMessage": "Send new DM",
"gotoDirectMessage": "Go to DM", "gotoDirectMessage": "Go to DM",
"react": "React", "react": "React",
@@ -352,6 +359,8 @@
"postTitle": "Title", "postTitle": "Title",
"postDescription": "Description", "postDescription": "Description",
"call": "Call", "call": "Call",
"callLeave": "Leave",
"callEnd": "End this call",
"done": "Done", "done": "Done",
"loginResetPasswordSent": "Password reset link sent, please check your email inbox.", "loginResetPasswordSent": "Password reset link sent, please check your email inbox.",
"accountDeletion": "Delete Account", "accountDeletion": "Delete Account",
@@ -622,8 +631,8 @@
"chatJoin": "Join the Chat", "chatJoin": "Join the Chat",
"realmJoin": "Join the Realm", "realmJoin": "Join the Realm",
"realmJoinSuccess": "Successfully joined the realm.", "realmJoinSuccess": "Successfully joined the realm.",
"discoverRealms": "Discover Realms", "discoverRealms": "Discover realms",
"discoverPublishers": "Discover Publishers", "discoverPublishers": "Discover publishers",
"search": "Search", "search": "Search",
"publisherMembers": "Collaborators", "publisherMembers": "Collaborators",
"developerHub": "Developer Hub", "developerHub": "Developer Hub",
@@ -702,5 +711,81 @@
"aboutDeviceName": "Device Name", "aboutDeviceName": "Device Name",
"aboutDeviceIdentifier": "Device Identifier", "aboutDeviceIdentifier": "Device Identifier",
"donate": "Donate", "donate": "Donate",
"donateDescription": "Support us to continue developing the Solar Network and keep the server up and running." "donateDescription": "Support us to continue developing the Solar Network and keep the server up and running.",
"fileId": "File ID",
"fileIdHint": "The file ID is the ID you get after upload the file via the Solar Network Drive.",
"translate": "Translate",
"translating": "Translating",
"translated": "Translated",
"reactionThumbUp": "Thumbs Up",
"reactionThumbDown": "Thumbs Down",
"reactionJustOkay": "Just Okay",
"reactionCry": "Cry",
"reactionConfuse": "Confused",
"reactionClap": "Clap",
"reactionLaugh": "Laugh",
"reactionAngry": "Angry",
"reactionParty": "Party",
"reactionPray": "Pray",
"reactionHeart": "Heart",
"selectMicrophone": "Select Microphone",
"selectCamera": "Select Camera",
"switchedTo": "Switched to {}",
"connecting": "Connecting",
"reconnecting": "Reconnecting",
"disconnected": "Disconnected",
"connected": "Connected",
"repliesLoadMore": "Load more replies",
"attachmentsRecentUploads": "Recent Uploads",
"attachmentsManualInput": "Manual Input",
"crop": "Crop",
"rename": "Rename",
"markAsSensitive": "Mark as Sensitive",
"fileName": "File name",
"sensitiveCategories.language": "Language",
"sensitiveCategories.sexualContent": "Sexual Content",
"sensitiveCategories.violence": "Violence",
"sensitiveCategories.profanity": "Profanity",
"sensitiveCategories.hateSpeech": "Hate Speech",
"sensitiveCategories.racism": "Racism",
"sensitiveCategories.adultContent": "Adult Content",
"sensitiveCategories.drugAbuse": "Drug Abuse",
"sensitiveCategories.alcoholAbuse": "Alcohol Abuse",
"sensitiveCategories.gambling": "Gambling",
"sensitiveCategories.selfHarm": "Self-harm",
"sensitiveCategories.childAbuse": "Child Abuse",
"sensitiveCategories.other": "Other",
"poll": "Poll",
"pollsRecent": "Recent Polls",
"pollCreateNew": "Create New",
"pollCreateNewHint": "Create a new poll for your post. Pick a publisher and continue.",
"publisher": "Publisher",
"publisherHint": "Enter the publisher name",
"publisherCannotBeEmpty": "Publisher cannot be empty",
"operationFailed": "Operation failed: {}",
"stickerMarketplace": "Sticker Marketplace",
"stickerPackAdded": "Sticker pack added to your collection",
"stickerPackRemoved": "Sticker pack removed from your collection",
"addPack": "Add Pack",
"removePack": "Remove Pack",
"browseAndAddStickers": "Browse and add sticker packs",
"stickerPack": "Sticker Pack",
"postCategoryTechnology": "Technology",
"postCategoryTravel": "Travel",
"postCategoryFood": "Food",
"postCategoryHealth": "Health",
"postCategoryScience": "Science",
"postCategorySports": "Sports",
"postCategoryFinance": "Finance",
"postCategoryLife": "Life",
"postCategoryArt": "Art",
"postCategoryStudy": "Study",
"postCategoryGaming": "Gaming",
"postCategoryProgramming": "Programming",
"postCategoryMusic": "Music",
"links": "Links",
"addLink": "Add link",
"linkKey": "Link Name",
"linkValue": "URL",
"debugOptions": "Debug Options"
} }

View File

@@ -120,9 +120,14 @@
"other": "{}个附件" "other": "{}个附件"
}, },
"edited": "已编辑", "edited": "已编辑",
"editedAt": "编辑于 {}",
"addVideo": "添加视频", "addVideo": "添加视频",
"addPhoto": "添加照片", "addPhoto": "添加照片",
"addFile": "添加文件", "addFile": "添加文件",
"addAttachmentById": "通过 ID 添加附件",
"enterFileId": "输入文件 ID",
"fileIdCannotBeEmpty": "文件 ID 不能为空",
"failedToFetchFile": "获取文件失败: {}",
"createDirectMessage": "创建新私人消息", "createDirectMessage": "创建新私人消息",
"gotoDirectMessage": "前往私信", "gotoDirectMessage": "前往私信",
"react": "反应", "react": "反应",

View File

@@ -123,6 +123,10 @@
"addVideo": "新增影片", "addVideo": "新增影片",
"addPhoto": "新增照片", "addPhoto": "新增照片",
"addFile": "新增檔案", "addFile": "新增檔案",
"addAttachmentById": "透過 ID 新增附件",
"enterFileId": "輸入檔案 ID",
"fileIdCannotBeEmpty": "檔案 ID 不能為空",
"failedToFetchFile": "無法取得檔案: {}",
"createDirectMessage": "建立新私人訊息", "createDirectMessage": "建立新私人訊息",
"gotoDirectMessage": "Go to DM", "gotoDirectMessage": "Go to DM",
"react": "反應", "react": "反應",

View File

@@ -1 +1 @@
{"flutter":{"platforms":{"android":{"default":{"projectId":"solian-0x001","appId":"1:961776991058:android:a8d3f7995b0b8e86f4188b","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"solian-0x001","appId":"1:961776991058:ios:727229d368cc47e1f4188b","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"macos":{"default":{"projectId":"solian-0x001","appId":"1:961776991058:ios:727229d368cc47e1f4188b","uploadDebugSymbols":false,"fileOutput":"macos/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"solian-0x001","configurations":{"android":"1:961776991058:android:a8d3f7995b0b8e86f4188b","ios":"1:961776991058:ios:727229d368cc47e1f4188b","macos":"1:961776991058:ios:727229d368cc47e1f4188b","web":"1:961776991058:web:b91d12f2892a5609f4188b","windows":"1:961776991058:web:3a912c0eb14028e5f4188b"}}}}}} {"flutter":{"platforms":{"android":{"default":{"projectId":"solian-0x001","appId":"1:961776991058:android:a8d3f7995b0b8e86f4188b","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"solian-0x001","appId":"1:961776991058:ios:727229d368cc47e1f4188b","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"macos":{"default":{"projectId":"solian-0x001","appId":"1:961776991058:ios:727229d368cc47e1f4188b","uploadDebugSymbols":false,"fileOutput":"macos/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"solian-0x001","configurations":{"android":"1:961776991058:android:a8d3f7995b0b8e86f4188b","ios":"1:961776991058:ios:727229d368cc47e1f4188b","macos":"1:961776991058:ios:727229d368cc47e1f4188b","web":"1:961776991058:web:3a912c0eb14028e5f4188b","windows":"1:961776991058:web:3a912c0eb14028e5f4188b"}}}}}}

View File

@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project # Uncomment this line to define a global platform for your project
platform :ios, '13.0' platform :ios, '15.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true' ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@@ -40,33 +40,33 @@ PODS:
- file_picker (0.0.1): - file_picker (0.0.1):
- DKImagePickerController/PhotoGallery - DKImagePickerController/PhotoGallery
- Flutter - Flutter
- Firebase/CoreOnly (11.15.0): - Firebase/CoreOnly (12.0.0):
- FirebaseCore (~> 11.15.0) - FirebaseCore (~> 12.0.0)
- Firebase/Messaging (11.15.0): - Firebase/Messaging (12.0.0):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseMessaging (~> 11.15.0) - FirebaseMessaging (~> 12.0.0)
- firebase_core (3.15.2): - firebase_core (4.0.0):
- Firebase/CoreOnly (= 11.15.0) - Firebase/CoreOnly (= 12.0.0)
- Flutter - Flutter
- firebase_messaging (15.2.10): - firebase_messaging (16.0.0):
- Firebase/Messaging (= 11.15.0) - Firebase/Messaging (= 12.0.0)
- firebase_core - firebase_core
- Flutter - Flutter
- FirebaseCore (11.15.0): - FirebaseCore (12.0.0):
- FirebaseCoreInternal (~> 11.15.0) - FirebaseCoreInternal (~> 12.0.0)
- GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Logger (~> 8.1) - GoogleUtilities/Logger (~> 8.1)
- FirebaseCoreInternal (11.15.0): - FirebaseCoreInternal (12.0.0):
- "GoogleUtilities/NSData+zlib (~> 8.1)" - "GoogleUtilities/NSData+zlib (~> 8.1)"
- FirebaseInstallations (11.15.0): - FirebaseInstallations (12.0.0):
- FirebaseCore (~> 11.15.0) - FirebaseCore (~> 12.0.0)
- GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1)
- PromisesObjC (~> 2.4) - PromisesObjC (~> 2.4)
- FirebaseMessaging (11.15.0): - FirebaseMessaging (12.0.0):
- FirebaseCore (~> 11.15.0) - FirebaseCore (~> 12.0.0)
- FirebaseInstallations (~> 11.0) - FirebaseInstallations (~> 12.0.0)
- GoogleDataTransport (~> 10.0) - GoogleDataTransport (~> 10.1)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Reachability (~> 8.1) - GoogleUtilities/Reachability (~> 8.1)
@@ -93,9 +93,9 @@ PODS:
- flutter_udid (0.0.1): - flutter_udid (0.0.1):
- Flutter - Flutter
- SAMKeychain - SAMKeychain
- flutter_webrtc (0.14.0): - flutter_webrtc (1.0.0):
- Flutter - Flutter
- WebRTC-SDK (= 125.6422.07) - WebRTC-SDK (= 137.7151.02)
- gal (1.0.0): - gal (1.0.0):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
@@ -131,10 +131,10 @@ PODS:
- irondash_engine_context (0.0.1): - irondash_engine_context (0.0.1):
- Flutter - Flutter
- Kingfisher (8.5.0) - Kingfisher (8.5.0)
- livekit_client (2.4.9): - livekit_client (2.5.0):
- Flutter - Flutter
- flutter_webrtc - flutter_webrtc
- WebRTC-SDK (= 125.6422.07) - WebRTC-SDK (= 137.7151.02)
- local_auth_darwin (0.0.1): - local_auth_darwin (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
@@ -191,6 +191,8 @@ PODS:
- sqlite3/common - sqlite3/common
- sqlite3/rtree (3.50.3): - sqlite3/rtree (3.50.3):
- sqlite3/common - sqlite3/common
- sqlite3/session (3.50.3):
- sqlite3/common
- sqlite3_flutter_libs (0.0.1): - sqlite3_flutter_libs (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
@@ -200,6 +202,7 @@ PODS:
- sqlite3/math - sqlite3/math
- sqlite3/perf-threadsafe - sqlite3/perf-threadsafe
- sqlite3/rtree - sqlite3/rtree
- sqlite3/session
- super_native_extensions (0.0.1): - super_native_extensions (0.0.1):
- Flutter - Flutter
- SwiftyGif (5.4.5) - SwiftyGif (5.4.5)
@@ -209,7 +212,7 @@ PODS:
- Flutter - Flutter
- wakelock_plus (0.0.1): - wakelock_plus (0.0.1):
- Flutter - Flutter
- WebRTC-SDK (125.6422.07) - WebRTC-SDK (137.7151.02)
DEPENDENCIES: DEPENDENCIES:
- Alamofire - Alamofire
@@ -361,13 +364,13 @@ SPEC CHECKSUMS:
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e Firebase: 800d487043c0557d9faed71477a38d9aafb08a41
firebase_core: 995454a784ff288be5689b796deb9e9fa3601818 firebase_core: 633e1851ffe1b9ab875f6467a4f574c79cef02e4
firebase_messaging: f4a41dd102ac18b840eba3f39d67e77922d3f707 firebase_messaging: d17feef781edc84ebefe62624fb384358ad96361
FirebaseCore: efb3893e5b94f32b86e331e3bd6dadf18b66568e FirebaseCore: 055f4ab117d5964158c833f3d5e7ec6d91648d4a
FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4 FirebaseCoreInternal: dedc28e569a4be85f38f3d6af1070a2e12018d55
FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843 FirebaseInstallations: d4c7c958f99c8860d7fcece786314ae790e2f988
FirebaseMessaging: 3b26e2cee503815e01c3701236b020aa9b576f09 FirebaseMessaging: af49f8d7c0a3d2a017d9302c80946f45a7777dde
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99 flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
flutter_keyboard_visibility: 4625131e43015dbbe759d9b20daaf77e0e3f6619 flutter_keyboard_visibility: 4625131e43015dbbe759d9b20daaf77e0e3f6619
@@ -376,14 +379,14 @@ SPEC CHECKSUMS:
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13 flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
flutter_timezone: 7c838e17ffd4645d261e87037e5bebf6d38fe544 flutter_timezone: 7c838e17ffd4645d261e87037e5bebf6d38fe544
flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9 flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9
flutter_webrtc: fd0d3bdef8766a0736dbbe2e5b7e85f1f3c52117 flutter_webrtc: 6f7da106613d52ade777d5b4875a43f48c28b457
gal: baecd024ebfd13c441269ca7404792a7152fde89 gal: baecd024ebfd13c441269ca7404792a7152fde89
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486 irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486
Kingfisher: ff0d31a1f07bdff6a1ebb3ba08b8e6e567b6500c Kingfisher: ff0d31a1f07bdff6a1ebb3ba08b8e6e567b6500c
livekit_client: 3f79d79233a5bd13d5b541732624ef959d7c538e livekit_client: e3b79b99405428aac439b6b76a254cd9a11dbbfb
local_auth_darwin: d2e8c53ef0c4f43c646462e3415432c4dab3ae19 local_auth_darwin: d2e8c53ef0c4f43c646462e3415432c4dab3ae19
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854 media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474 media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
@@ -404,14 +407,14 @@ SPEC CHECKSUMS:
sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418 sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
sqlite3: 83105acd294c9137c026e2da1931c30b4588ab81 sqlite3: 83105acd294c9137c026e2da1931c30b4588ab81
sqlite3_flutter_libs: ce0522d143cee6ef5e16587acfce8f476316e005 sqlite3_flutter_libs: 616267f2fca40e9c6af8c5d82324e05667040b6e
super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4 super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12 volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556 wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
WebRTC-SDK: dff00a3892bc570b6014e046297782084071657e WebRTC-SDK: d20de357dcbf7c9696b124b39f3ff62125107e4b
PODFILE CHECKSUM: f6df17c2a0cbd7af89692fd3877231eaea40230f PODFILE CHECKSUM: c818292390b02fa379036ea099713a332bd7193f
COCOAPODS: 1.16.2 COCOAPODS: 1.16.2

View File

@@ -10,6 +10,8 @@
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
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, ); }; };
73C305D82E0BE878009035B9 /* SolianShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 73C305CE2E0BE878009035B9 /* SolianShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 73C305D82E0BE878009035B9 /* SolianShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 73C305CE2E0BE878009035B9 /* SolianShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
73CDD6812DEC00480059D95D /* SolianNotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 73CDD67A2DEC00480059D95D /* SolianNotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 73CDD6812DEC00480059D95D /* SolianNotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 73CDD67A2DEC00480059D95D /* SolianNotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
73D4264B2DEB815D006C0AAE /* NotifyDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73D4264A2DEB815D006C0AAE /* NotifyDelegate.swift */; }; 73D4264B2DEB815D006C0AAE /* NotifyDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73D4264A2DEB815D006C0AAE /* NotifyDelegate.swift */; };
@@ -32,6 +34,13 @@
remoteGlobalIDString = 97C146ED1CF9000F007C117D; remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner; remoteInfo = Runner;
}; };
73ACDFC12E3D0E6100B63535 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 73ACDFAA2E3D0E6100B63535;
remoteInfo = SolianBroadcastExtension;
};
73C305D62E0BE878009035B9 /* PBXContainerItemProxy */ = { 73C305D62E0BE878009035B9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */; containerPortal = 97C146E61CF9000F007C117D /* Project object */;
@@ -55,6 +64,7 @@
dstPath = ""; dstPath = "";
dstSubfolderSpec = 13; dstSubfolderSpec = 13;
files = ( files = (
73ACDFC32E3D0E6100B63535 /* SolianBroadcastExtension.appex in Embed Foundation Extensions */,
73C305D82E0BE878009035B9 /* SolianShareExtension.appex in Embed Foundation Extensions */, 73C305D82E0BE878009035B9 /* SolianShareExtension.appex in Embed Foundation Extensions */,
73CDD6812DEC00480059D95D /* SolianNotificationService.appex in Embed Foundation Extensions */, 73CDD6812DEC00480059D95D /* SolianNotificationService.appex in Embed Foundation Extensions */,
); );
@@ -91,6 +101,9 @@
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>"; }; 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>"; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
737E920B2DB6A9FF00BE9CDB /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; }; 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; };
73ACDFAC2E3D0E6100B63535 /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = System/Library/Frameworks/ReplayKit.framework; sourceTree = SDKROOT; };
73ACDFB82E3D0E6100B63535 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
73C305CE2E0BE878009035B9 /* SolianShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SolianShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 73C305CE2E0BE878009035B9 /* SolianShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SolianShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
73CDD67A2DEC00480059D95D /* SolianNotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SolianNotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 73CDD67A2DEC00480059D95D /* SolianNotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SolianNotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
73D4264A2DEB815D006C0AAE /* NotifyDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotifyDelegate.swift; sourceTree = "<group>"; }; 73D4264A2DEB815D006C0AAE /* NotifyDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotifyDelegate.swift; sourceTree = "<group>"; };
@@ -117,6 +130,13 @@
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
73ACDFCA2E3D0E6100B63535 /* Exceptions for "SolianBroadcastExtension" folder in "SolianBroadcastExtension" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
);
target = 73ACDFAA2E3D0E6100B63535 /* SolianBroadcastExtension */;
};
73C305DC2E0BE878009035B9 /* Exceptions for "SolianShareExtension" folder in "SolianShareExtension" target */ = { 73C305DC2E0BE878009035B9 /* Exceptions for "SolianShareExtension" folder in "SolianShareExtension" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet; isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = ( membershipExceptions = (
@@ -150,6 +170,14 @@
path = Services; path = Services;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
73ACDFAE2E3D0E6100B63535 /* SolianBroadcastExtension */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
73ACDFCA2E3D0E6100B63535 /* Exceptions for "SolianBroadcastExtension" folder in "SolianBroadcastExtension" target */,
);
path = SolianBroadcastExtension;
sourceTree = "<group>";
};
73C305CF2E0BE878009035B9 /* SolianShareExtension */ = { 73C305CF2E0BE878009035B9 /* SolianShareExtension */ = {
isa = PBXFileSystemSynchronizedRootGroup; isa = PBXFileSystemSynchronizedRootGroup;
exceptions = ( exceptions = (
@@ -177,6 +205,14 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
73ACDFA82E3D0E6100B63535 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
73ACDFAD2E3D0E6100B63535 /* ReplayKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
73C305CB2E0BE878009035B9 /* Frameworks */ = { 73C305CB2E0BE878009035B9 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -220,6 +256,8 @@
AA0CA8A3E15DEE023BB27438 /* Pods_NotificationService.framework */, AA0CA8A3E15DEE023BB27438 /* Pods_NotificationService.framework */,
39FE4CC6223F0D3C0E1FFD04 /* Pods_SolianNotificationService.framework */, 39FE4CC6223F0D3C0E1FFD04 /* Pods_SolianNotificationService.framework */,
7B40764A2C4CC0E7DC70A0D3 /* Pods_SolianShareExtension.framework */, 7B40764A2C4CC0E7DC70A0D3 /* Pods_SolianShareExtension.framework */,
73ACDFAC2E3D0E6100B63535 /* ReplayKit.framework */,
73ACDFB82E3D0E6100B63535 /* UIKit.framework */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -264,6 +302,7 @@
97C146F01CF9000F007C117D /* Runner */, 97C146F01CF9000F007C117D /* Runner */,
73CDD67B2DEC00480059D95D /* SolianNotificationService */, 73CDD67B2DEC00480059D95D /* SolianNotificationService */,
73C305CF2E0BE878009035B9 /* SolianShareExtension */, 73C305CF2E0BE878009035B9 /* SolianShareExtension */,
73ACDFAE2E3D0E6100B63535 /* SolianBroadcastExtension */,
97C146EF1CF9000F007C117D /* Products */, 97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */, 331C8082294A63A400263BE5 /* RunnerTests */,
91E124CE95BCB4DCD890160D /* Pods */, 91E124CE95BCB4DCD890160D /* Pods */,
@@ -279,6 +318,7 @@
331C8081294A63A400263BE5 /* RunnerTests.xctest */, 331C8081294A63A400263BE5 /* RunnerTests.xctest */,
73CDD67A2DEC00480059D95D /* SolianNotificationService.appex */, 73CDD67A2DEC00480059D95D /* SolianNotificationService.appex */,
73C305CE2E0BE878009035B9 /* SolianShareExtension.appex */, 73C305CE2E0BE878009035B9 /* SolianShareExtension.appex */,
73ACDFAB2E3D0E6100B63535 /* SolianBroadcastExtension.appex */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -323,6 +363,26 @@
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test"; productType = "com.apple.product-type.bundle.unit-test";
}; };
73ACDFAA2E3D0E6100B63535 /* SolianBroadcastExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = 73ACDFCB2E3D0E6100B63535 /* Build configuration list for PBXNativeTarget "SolianBroadcastExtension" */;
buildPhases = (
73ACDFA72E3D0E6100B63535 /* Sources */,
73ACDFA82E3D0E6100B63535 /* Frameworks */,
73ACDFA92E3D0E6100B63535 /* Resources */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
73ACDFAE2E3D0E6100B63535 /* SolianBroadcastExtension */,
);
name = SolianBroadcastExtension;
productName = SolianBroadcastExtension;
productReference = 73ACDFAB2E3D0E6100B63535 /* SolianBroadcastExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
73C305CD2E0BE878009035B9 /* SolianShareExtension */ = { 73C305CD2E0BE878009035B9 /* SolianShareExtension */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 73C305DD2E0BE878009035B9 /* Build configuration list for PBXNativeTarget "SolianShareExtension" */; buildConfigurationList = 73C305DD2E0BE878009035B9 /* Build configuration list for PBXNativeTarget "SolianShareExtension" */;
@@ -385,6 +445,7 @@
dependencies = ( dependencies = (
73CDD6802DEC00480059D95D /* PBXTargetDependency */, 73CDD6802DEC00480059D95D /* PBXTargetDependency */,
73C305D72E0BE878009035B9 /* PBXTargetDependency */, 73C305D72E0BE878009035B9 /* PBXTargetDependency */,
73ACDFC22E3D0E6100B63535 /* PBXTargetDependency */,
); );
fileSystemSynchronizedGroups = ( fileSystemSynchronizedGroups = (
73268D272DEB012A0076E970 /* Services */, 73268D272DEB012A0076E970 /* Services */,
@@ -409,6 +470,9 @@
CreatedOnToolsVersion = 14.0; CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D; TestTargetID = 97C146ED1CF9000F007C117D;
}; };
73ACDFAA2E3D0E6100B63535 = {
CreatedOnToolsVersion = 16.4;
};
73C305CD2E0BE878009035B9 = { 73C305CD2E0BE878009035B9 = {
CreatedOnToolsVersion = 16.4; CreatedOnToolsVersion = 16.4;
}; };
@@ -438,6 +502,7 @@
331C8080294A63A400263BE5 /* RunnerTests */, 331C8080294A63A400263BE5 /* RunnerTests */,
73CDD6792DEC00480059D95D /* SolianNotificationService */, 73CDD6792DEC00480059D95D /* SolianNotificationService */,
73C305CD2E0BE878009035B9 /* SolianShareExtension */, 73C305CD2E0BE878009035B9 /* SolianShareExtension */,
73ACDFAA2E3D0E6100B63535 /* SolianBroadcastExtension */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
@@ -450,6 +515,13 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
73ACDFA92E3D0E6100B63535 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
73C305CC2E0BE878009035B9 /* Resources */ = { 73C305CC2E0BE878009035B9 /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -643,6 +715,13 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
73ACDFA72E3D0E6100B63535 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
73C305CA2E0BE878009035B9 /* Sources */ = { 73C305CA2E0BE878009035B9 /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -675,6 +754,11 @@
target = 97C146ED1CF9000F007C117D /* Runner */; target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
}; };
73ACDFC22E3D0E6100B63535 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 73ACDFAA2E3D0E6100B63535 /* SolianBroadcastExtension */;
targetProxy = 73ACDFC12E3D0E6100B63535 /* PBXContainerItemProxy */;
};
73C305D72E0BE878009035B9 /* PBXTargetDependency */ = { 73C305D72E0BE878009035B9 /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
target = 73C305CD2E0BE878009035B9 /* SolianShareExtension */; target = 73C305CD2E0BE878009035B9 /* SolianShareExtension */;
@@ -773,7 +857,7 @@
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Solian; INFOPLIST_KEY_CFBundleDisplayName = Solian;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@@ -836,6 +920,123 @@
}; };
name = Profile; name = Profile;
}; };
73ACDFC42E3D0E6100B63535 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
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 = SolianBroadcastExtension/SolianBroadcastExtension.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 = SolianBroadcastExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SolianBroadcastExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 15.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.SolianBroadcastExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
73ACDFC52E3D0E6100B63535 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
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 = SolianBroadcastExtension/SolianBroadcastExtension.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 = SolianBroadcastExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SolianBroadcastExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 15.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.SolianBroadcastExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
73ACDFC62E3D0E6100B63535 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
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 = SolianBroadcastExtension/SolianBroadcastExtension.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 = SolianBroadcastExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SolianBroadcastExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 15.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.SolianBroadcastExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Profile;
};
73C305D92E0BE878009035B9 /* Debug */ = { 73C305D92E0BE878009035B9 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 17FAB080A9C53193ABD9C15B /* Pods-SolianShareExtension.debug.xcconfig */; baseConfigurationReference = 17FAB080A9C53193ABD9C15B /* Pods-SolianShareExtension.debug.xcconfig */;
@@ -1204,7 +1405,7 @@
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Solian; INFOPLIST_KEY_CFBundleDisplayName = Solian;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@@ -1232,7 +1433,7 @@
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Solian; INFOPLIST_KEY_CFBundleDisplayName = Solian;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@@ -1258,6 +1459,16 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
73ACDFCB2E3D0E6100B63535 /* Build configuration list for PBXNativeTarget "SolianBroadcastExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
73ACDFC42E3D0E6100B63535 /* Debug */,
73ACDFC52E3D0E6100B63535 /* Release */,
73ACDFC62E3D0E6100B63535 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
73C305DD2E0BE878009035B9 /* Build configuration list for PBXNativeTarget "SolianShareExtension" */ = { 73C305DD2E0BE878009035B9 /* Build configuration list for PBXNativeTarget "SolianShareExtension" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (

View File

@@ -2,6 +2,12 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CLIENT_ID</key>
<string>961776991058-stt7et4qvn3cpscl4r61gl1hnlatqkig.apps.googleusercontent.com</string>
<key>REVERSED_CLIENT_ID</key>
<string>com.googleusercontent.apps.961776991058-stt7et4qvn3cpscl4r61gl1hnlatqkig</string>
<key>ANDROID_CLIENT_ID</key>
<string>961776991058-r4iv9qoio57ul7utbfpgfrda2etvtch8.apps.googleusercontent.com</string>
<key>API_KEY</key> <key>API_KEY</key>
<string>AIzaSyCzQIyiYKoYHTpGXhN-IjgMML8z797WVD8</string> <string>AIzaSyCzQIyiYKoYHTpGXhN-IjgMML8z797WVD8</string>
<key>GCM_SENDER_ID</key> <key>GCM_SENDER_ID</key>

View File

@@ -0,0 +1,37 @@
//
// Atomic.swift
// Broadcast Extension
//
// Created by Maksym Shcheglov.
// https://www.onswiftwings.com/posts/atomic-property-wrapper/
//
import Foundation
@propertyWrapper
struct Atomic<Value> {
private var value: Value
private let lock = NSLock()
init(wrappedValue value: Value) {
self.value = value
}
var wrappedValue: Value {
get { load() }
set { store(newValue: newValue) }
}
func load() -> Value {
lock.lock()
defer { lock.unlock() }
return value
}
mutating func store(newValue: Value) {
lock.lock()
defer { lock.unlock() }
value = newValue
}
}

View File

@@ -0,0 +1,29 @@
//
// DarwinNotificationCenter.swift
// Broadcast Extension
//
// Created by Alex-Dan Bumbu on 23/03/2021.
// Copyright © 2021 8x8, Inc. All rights reserved.
//
import Foundation
enum DarwinNotification: String {
case broadcastStarted = "iOS_BroadcastStarted"
case broadcastStopped = "iOS_BroadcastStopped"
}
class DarwinNotificationCenter {
static let shared = DarwinNotificationCenter()
private let notificationCenter: CFNotificationCenter
init() {
notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
}
func postNotification(_ name: DarwinNotification) {
CFNotificationCenterPostNotification(notificationCenter, CFNotificationName(rawValue: name.rawValue as CFString), nil, nil, true)
}
}

View File

@@ -0,0 +1,15 @@
<?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.broadcast-services-upload</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).SampleHandler</string>
<key>RPBroadcastProcessMode</key>
<string>RPBroadcastProcessModeSampleBuffer</string>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,103 @@
//
// SampleHandler.swift
// Broadcast Extension
//
// Created by Alex-Dan Bumbu on 04.06.2021.
//
import ReplayKit
import OSLog
let broadcastLogger = OSLog(subsystem: "dev.solsynth.solian", category: "Broadcast")
private enum Constants {
// the App Group ID value that the app and the broadcast extension targets are setup with. It differs for each app.
static let appGroupIdentifier = "group.solsynth.solian"
}
class SampleHandler: RPBroadcastSampleHandler {
private var clientConnection: SocketConnection?
private var uploader: SampleUploader?
private var frameCount: Int = 0
var socketFilePath: String {
let sharedContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupIdentifier)
return sharedContainer?.appendingPathComponent("rtc_SSFD").path ?? ""
}
override init() {
super.init()
if let connection = SocketConnection(filePath: socketFilePath) {
clientConnection = connection
setupConnection()
uploader = SampleUploader(connection: connection)
}
os_log(.debug, log: broadcastLogger, "%{public}s", socketFilePath)
}
override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {
// User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional.
frameCount = 0
DarwinNotificationCenter.shared.postNotification(.broadcastStarted)
openConnection()
}
override func broadcastPaused() {
// User has requested to pause the broadcast. Samples will stop being delivered.
}
override func broadcastResumed() {
// User has requested to resume the broadcast. Samples delivery will resume.
}
override func broadcastFinished() {
// User has requested to finish the broadcast.
DarwinNotificationCenter.shared.postNotification(.broadcastStopped)
clientConnection?.close()
}
override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {
switch sampleBufferType {
case RPSampleBufferType.video:
uploader?.send(sample: sampleBuffer)
default:
break
}
}
}
private extension SampleHandler {
func setupConnection() {
clientConnection?.didClose = { [weak self] error in
os_log(.debug, log: broadcastLogger, "client connection did close \(String(describing: error))")
if let error = error {
self?.finishBroadcastWithError(error)
} else {
// the displayed failure message is more user friendly when using NSError instead of Error
let JMScreenSharingStopped = 10001
let customError = NSError(domain: RPRecordingErrorDomain, code: JMScreenSharingStopped, userInfo: [NSLocalizedDescriptionKey: "Screen sharing stopped"])
self?.finishBroadcastWithError(customError)
}
}
}
func openConnection() {
let queue = DispatchQueue(label: "broadcast.connectTimer")
let timer = DispatchSource.makeTimerSource(queue: queue)
timer.schedule(deadline: .now(), repeating: .milliseconds(100), leeway: .milliseconds(500))
timer.setEventHandler { [weak self] in
guard self?.clientConnection?.open() == true else {
return
}
timer.cancel()
}
timer.resume()
}
}

View File

@@ -0,0 +1,147 @@
//
// SampleUploader.swift
// Broadcast Extension
//
// Created by Alex-Dan Bumbu on 22/03/2021.
// Copyright © 2021 8x8, Inc. All rights reserved.
//
import Foundation
import ReplayKit
import OSLog
private enum Constants {
static let bufferMaxLength = 10240
}
class SampleUploader {
private static var imageContext = CIContext(options: nil)
@Atomic private var isReady = false
private var connection: SocketConnection
private var dataToSend: Data?
private var byteIndex = 0
private let serialQueue: DispatchQueue
init(connection: SocketConnection) {
self.connection = connection
self.serialQueue = DispatchQueue(label: "org.jitsi.meet.broadcast.sampleUploader")
setupConnection()
}
@discardableResult func send(sample buffer: CMSampleBuffer) -> Bool {
guard isReady else {
return false
}
isReady = false
dataToSend = prepare(sample: buffer)
byteIndex = 0
serialQueue.async { [weak self] in
self?.sendDataChunk()
}
return true
}
}
private extension SampleUploader {
func setupConnection() {
connection.didOpen = { [weak self] in
self?.isReady = true
}
connection.streamHasSpaceAvailable = { [weak self] in
self?.serialQueue.async {
if let success = self?.sendDataChunk() {
self?.isReady = !success
}
}
}
}
@discardableResult func sendDataChunk() -> Bool {
guard let dataToSend = dataToSend else {
return false
}
var bytesLeft = dataToSend.count - byteIndex
var length = bytesLeft > Constants.bufferMaxLength ? Constants.bufferMaxLength : bytesLeft
length = dataToSend[byteIndex..<(byteIndex + length)].withUnsafeBytes {
guard let ptr = $0.bindMemory(to: UInt8.self).baseAddress else {
return 0
}
return connection.writeToStream(buffer: ptr, maxLength: length)
}
if length > 0 {
byteIndex += length
bytesLeft -= length
if bytesLeft == 0 {
self.dataToSend = nil
byteIndex = 0
}
} else {
os_log(.debug, log: broadcastLogger, "writeBufferToStream failure")
}
return true
}
func prepare(sample buffer: CMSampleBuffer) -> Data? {
guard let imageBuffer = CMSampleBufferGetImageBuffer(buffer) else {
os_log(.debug, log: broadcastLogger, "image buffer not available")
return nil
}
CVPixelBufferLockBaseAddress(imageBuffer, .readOnly)
let scaleFactor = 1.0
let width = CVPixelBufferGetWidth(imageBuffer)/Int(scaleFactor)
let height = CVPixelBufferGetHeight(imageBuffer)/Int(scaleFactor)
let orientation = CMGetAttachment(buffer, key: RPVideoSampleOrientationKey as CFString, attachmentModeOut: nil)?.uintValue ?? 0
let scaleTransform = CGAffineTransform(scaleX: CGFloat(1.0/scaleFactor), y: CGFloat(1.0/scaleFactor))
let bufferData = self.jpegData(from: imageBuffer, scale: scaleTransform)
CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly)
guard let messageData = bufferData else {
os_log(.debug, log: broadcastLogger, "corrupted image buffer")
return nil
}
let httpResponse = CFHTTPMessageCreateResponse(nil, 200, nil, kCFHTTPVersion1_1).takeRetainedValue()
CFHTTPMessageSetHeaderFieldValue(httpResponse, "Content-Length" as CFString, String(messageData.count) as CFString)
CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Width" as CFString, String(width) as CFString)
CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Height" as CFString, String(height) as CFString)
CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Orientation" as CFString, String(orientation) as CFString)
CFHTTPMessageSetBody(httpResponse, messageData as CFData)
let serializedMessage = CFHTTPMessageCopySerializedMessage(httpResponse)?.takeRetainedValue() as Data?
return serializedMessage
}
func jpegData(from buffer: CVPixelBuffer, scale scaleTransform: CGAffineTransform) -> Data? {
let image = CIImage(cvPixelBuffer: buffer).transformed(by: scaleTransform)
guard let colorSpace = image.colorSpace else {
return nil
}
let options: [CIImageRepresentationOption: Float] = [kCGImageDestinationLossyCompressionQuality as CIImageRepresentationOption: 1.0]
return SampleUploader.imageContext.jpegRepresentation(of: image, colorSpace: colorSpace, options: options)
}
}

View File

@@ -0,0 +1,199 @@
//
// SocketConnection.swift
// Broadcast Extension
//
// Created by Alex-Dan Bumbu on 22/03/2021.
// Copyright © 2021 Atlassian Inc. All rights reserved.
//
import Foundation
import OSLog
class SocketConnection: NSObject {
var didOpen: (() -> Void)?
var didClose: ((Error?) -> Void)?
var streamHasSpaceAvailable: (() -> Void)?
private let filePath: String
private var socketHandle: Int32 = -1
private var address: sockaddr_un?
private var inputStream: InputStream?
private var outputStream: OutputStream?
private var networkQueue: DispatchQueue?
private var shouldKeepRunning = false
init?(filePath path: String) {
filePath = path
socketHandle = Darwin.socket(AF_UNIX, SOCK_STREAM, 0)
guard socketHandle != -1 else {
os_log(.debug, log: broadcastLogger, "failure: create socket")
return nil
}
}
func open() -> Bool {
os_log(.debug, log: broadcastLogger, "open socket connection")
guard FileManager.default.fileExists(atPath: filePath) else {
os_log(.debug, log: broadcastLogger, "failure: socket file missing")
return false
}
guard setupAddress() == true else {
return false
}
guard connectSocket() == true else {
return false
}
setupStreams()
inputStream?.open()
outputStream?.open()
return true
}
func close() {
unscheduleStreams()
inputStream?.delegate = nil
outputStream?.delegate = nil
inputStream?.close()
outputStream?.close()
inputStream = nil
outputStream = nil
}
func writeToStream(buffer: UnsafePointer<UInt8>, maxLength length: Int) -> Int {
outputStream?.write(buffer, maxLength: length) ?? 0
}
}
extension SocketConnection: StreamDelegate {
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case .openCompleted:
os_log(.debug, log: broadcastLogger, "client stream open completed")
if aStream == outputStream {
didOpen?()
}
case .hasBytesAvailable:
if aStream == inputStream {
var buffer: UInt8 = 0
let numberOfBytesRead = inputStream?.read(&buffer, maxLength: 1)
if numberOfBytesRead == 0 && aStream.streamStatus == .atEnd {
os_log(.debug, log: broadcastLogger, "server socket closed")
close()
notifyDidClose(error: nil)
}
}
case .hasSpaceAvailable:
if aStream == outputStream {
streamHasSpaceAvailable?()
}
case .errorOccurred:
os_log(.debug, log: broadcastLogger, "client stream error occured: \(String(describing: aStream.streamError))")
close()
notifyDidClose(error: aStream.streamError)
default:
break
}
}
}
private extension SocketConnection {
func setupAddress() -> Bool {
var addr = sockaddr_un()
guard filePath.count < MemoryLayout.size(ofValue: addr.sun_path) else {
os_log(.debug, log: broadcastLogger, "failure: fd path is too long")
return false
}
_ = withUnsafeMutablePointer(to: &addr.sun_path.0) { ptr in
filePath.withCString {
strncpy(ptr, $0, filePath.count)
}
}
address = addr
return true
}
func connectSocket() -> Bool {
guard var addr = address else {
return false
}
let status = withUnsafePointer(to: &addr) { ptr in
ptr.withMemoryRebound(to: sockaddr.self, capacity: 1) {
Darwin.connect(socketHandle, $0, socklen_t(MemoryLayout<sockaddr_un>.size))
}
}
guard status == noErr else {
os_log(.debug, log: broadcastLogger, "failure: \(status)")
return false
}
return true
}
func setupStreams() {
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocket(kCFAllocatorDefault, socketHandle, &readStream, &writeStream)
inputStream = readStream?.takeRetainedValue()
inputStream?.delegate = self
inputStream?.setProperty(kCFBooleanTrue, forKey: Stream.PropertyKey(kCFStreamPropertyShouldCloseNativeSocket as String))
outputStream = writeStream?.takeRetainedValue()
outputStream?.delegate = self
outputStream?.setProperty(kCFBooleanTrue, forKey: Stream.PropertyKey(kCFStreamPropertyShouldCloseNativeSocket as String))
scheduleStreams()
}
func scheduleStreams() {
shouldKeepRunning = true
networkQueue = DispatchQueue.global(qos: .userInitiated)
networkQueue?.async { [weak self] in
self?.inputStream?.schedule(in: .current, forMode: .common)
self?.outputStream?.schedule(in: .current, forMode: .common)
RunLoop.current.run()
var isRunning = false
repeat {
isRunning = self?.shouldKeepRunning ?? false && RunLoop.current.run(mode: .default, before: .distantFuture)
} while (isRunning)
}
}
func unscheduleStreams() {
networkQueue?.sync { [weak self] in
self?.inputStream?.remove(from: .current, forMode: .common)
self?.outputStream?.remove(from: .current, forMode: .common)
}
shouldKeepRunning = false
}
func notifyDidClose(error: Error?) {
if didClose != nil {
didClose?(error)
}
}
}

View File

@@ -0,0 +1,10 @@
<?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>
</dict>
</plist>

View File

@@ -49,8 +49,7 @@ class AppDatabase extends _$AppDatabase {
} }
Future<int> updateMessage(ChatMessagesCompanion message) { Future<int> updateMessage(ChatMessagesCompanion message) {
return (update(chatMessages) return into(chatMessages).insert(message, mode: InsertMode.insertOrReplace);
..where((m) => m.id.equals(message.id.value))).write(message);
} }
Future<int> updateMessageStatus(String id, MessageStatus status) { Future<int> updateMessageStatus(String id, MessageStatus status) {

View File

@@ -29,10 +29,7 @@ class DefaultFirebaseOptions {
case TargetPlatform.windows: case TargetPlatform.windows:
return windows; return windows;
case TargetPlatform.linux: case TargetPlatform.linux:
throw UnsupportedError( return windows;
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default: default:
throw UnsupportedError( throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.', 'DefaultFirebaseOptions are not supported for this platform.',
@@ -41,13 +38,13 @@ class DefaultFirebaseOptions {
} }
static const FirebaseOptions web = FirebaseOptions( static const FirebaseOptions web = FirebaseOptions(
apiKey: 'AIzaSyBKfIQpTouj5rXnlzkEieSlbAzepm4mgJE', apiKey: 'AIzaSyCfgOdlcr7h8x8j0WKx_S2wXnGkOopq320',
appId: '1:961776991058:web:b91d12f2892a5609f4188b', appId: '1:961776991058:web:3a912c0eb14028e5f4188b',
messagingSenderId: '961776991058', messagingSenderId: '961776991058',
projectId: 'solian-0x001', projectId: 'solian-0x001',
authDomain: 'solian-0x001.firebaseapp.com', authDomain: 'solian-0x001.firebaseapp.com',
storageBucket: 'solian-0x001.firebasestorage.app', storageBucket: 'solian-0x001.firebasestorage.app',
measurementId: 'G-XY3HHKG0PE', measurementId: 'G-JD1YEG9D6F',
); );
static const FirebaseOptions android = FirebaseOptions( static const FirebaseOptions android = FirebaseOptions(
@@ -64,6 +61,10 @@ class DefaultFirebaseOptions {
messagingSenderId: '961776991058', messagingSenderId: '961776991058',
projectId: 'solian-0x001', projectId: 'solian-0x001',
storageBucket: 'solian-0x001.firebasestorage.app', storageBucket: 'solian-0x001.firebasestorage.app',
androidClientId:
'961776991058-r4iv9qoio57ul7utbfpgfrda2etvtch8.apps.googleusercontent.com',
iosClientId:
'961776991058-stt7et4qvn3cpscl4r61gl1hnlatqkig.apps.googleusercontent.com',
iosBundleId: 'dev.solsynth.solian', iosBundleId: 'dev.solsynth.solian',
); );
@@ -73,6 +74,10 @@ class DefaultFirebaseOptions {
messagingSenderId: '961776991058', messagingSenderId: '961776991058',
projectId: 'solian-0x001', projectId: 'solian-0x001',
storageBucket: 'solian-0x001.firebasestorage.app', storageBucket: 'solian-0x001.firebasestorage.app',
androidClientId:
'961776991058-r4iv9qoio57ul7utbfpgfrda2etvtch8.apps.googleusercontent.com',
iosClientId:
'961776991058-stt7et4qvn3cpscl4r61gl1hnlatqkig.apps.googleusercontent.com',
iosBundleId: 'dev.solsynth.solian', iosBundleId: 'dev.solsynth.solian',
); );
@@ -85,5 +90,4 @@ class DefaultFirebaseOptions {
storageBucket: 'solian-0x001.firebasestorage.app', storageBucket: 'solian-0x001.firebasestorage.app',
measurementId: 'G-JD1YEG9D6F', measurementId: 'G-JD1YEG9D6F',
); );
} }

View File

@@ -20,7 +20,6 @@ import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/pods/userinfo.dart';
import 'package:island/pods/websocket.dart'; import 'package:island/pods/websocket.dart';
import 'package:island/route.dart'; import 'package:island/route.dart';
import 'package:island/services/notify.dart'; import 'package:island/services/notify.dart';
import 'package:island/services/timezone.dart'; import 'package:island/services/timezone.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
@@ -30,6 +29,8 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
import 'package:flutter_langdetect/flutter_langdetect.dart' as langdetect;
import 'package:island/services/update_service.dart';
@pragma('vm:entry-point') @pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async { Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
@@ -51,11 +52,18 @@ void main() async {
} }
try { try {
await langdetect.initLangDetect();
await EasyLocalization.ensureInitialized(); await EasyLocalization.ensureInitialized();
if (kIsWeb || !Platform.isLinux) {
await Firebase.initializeApp( await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform, options: DefaultFirebaseOptions.currentPlatform,
); );
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); FirebaseMessaging.onBackgroundMessage(
_firebaseMessagingBackgroundHandler,
);
}
log("[SplashScreen] Firebase is ready!"); log("[SplashScreen] Firebase is ready!");
} catch (err) { } catch (err) {
showErrorAlert(err); showErrorAlert(err);
@@ -136,6 +144,15 @@ void main() async {
), ),
), ),
); );
// Schedule update check shortly after startup, when a context is available.
// Uses the global overlay key to obtain a BuildContext safely.
WidgetsBinding.instance.addPostFrameCallback((_) {
final ctx = globalOverlay.currentContext;
if (ctx != null) {
UpdateService().checkForUpdates(ctx);
}
});
} }
// Router will be provided through Riverpod // Router will be provided through Riverpod

View File

@@ -162,8 +162,6 @@ sealed class CallParticipant with _$CallParticipant {
required String identity, required String identity,
required String name, required String name,
required DateTime joinedAt, required DateTime joinedAt,
required String? accountId,
required SnChatMember? profile,
}) = _CallParticipant; }) = _CallParticipant;
factory CallParticipant.fromJson(Map<String, dynamic> json) => factory CallParticipant.fromJson(Map<String, dynamic> json) =>

View File

@@ -2498,7 +2498,7 @@ as List<CallParticipant>,
/// @nodoc /// @nodoc
mixin _$CallParticipant { mixin _$CallParticipant {
String get identity; String get name; DateTime get joinedAt; String? get accountId; SnChatMember? get profile; String get identity; String get name; DateTime get joinedAt;
/// Create a copy of CallParticipant /// Create a copy of CallParticipant
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -2511,16 +2511,16 @@ $CallParticipantCopyWith<CallParticipant> get copyWith => _$CallParticipantCopyW
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is CallParticipant&&(identical(other.identity, identity) || other.identity == identity)&&(identical(other.name, name) || other.name == name)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.profile, profile) || other.profile == profile)); return identical(this, other) || (other.runtimeType == runtimeType&&other is CallParticipant&&(identical(other.identity, identity) || other.identity == identity)&&(identical(other.name, name) || other.name == name)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,identity,name,joinedAt,accountId,profile); int get hashCode => Object.hash(runtimeType,identity,name,joinedAt);
@override @override
String toString() { String toString() {
return 'CallParticipant(identity: $identity, name: $name, joinedAt: $joinedAt, accountId: $accountId, profile: $profile)'; return 'CallParticipant(identity: $identity, name: $name, joinedAt: $joinedAt)';
} }
@@ -2531,11 +2531,11 @@ abstract mixin class $CallParticipantCopyWith<$Res> {
factory $CallParticipantCopyWith(CallParticipant value, $Res Function(CallParticipant) _then) = _$CallParticipantCopyWithImpl; factory $CallParticipantCopyWith(CallParticipant value, $Res Function(CallParticipant) _then) = _$CallParticipantCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String identity, String name, DateTime joinedAt, String? accountId, SnChatMember? profile String identity, String name, DateTime joinedAt
}); });
$SnChatMemberCopyWith<$Res>? get profile;
} }
/// @nodoc /// @nodoc
@@ -2548,29 +2548,15 @@ class _$CallParticipantCopyWithImpl<$Res>
/// Create a copy of CallParticipant /// Create a copy of CallParticipant
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? identity = null,Object? name = null,Object? joinedAt = null,Object? accountId = freezed,Object? profile = freezed,}) { @pragma('vm:prefer-inline') @override $Res call({Object? identity = null,Object? name = null,Object? joinedAt = null,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
identity: null == identity ? _self.identity : identity // ignore: cast_nullable_to_non_nullable identity: null == identity ? _self.identity : identity // ignore: cast_nullable_to_non_nullable
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String,joinedAt: null == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable as String,joinedAt: null == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable
as DateTime,accountId: freezed == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable as DateTime,
as String?,profile: freezed == profile ? _self.profile : profile // ignore: cast_nullable_to_non_nullable
as SnChatMember?,
)); ));
} }
/// Create a copy of CallParticipant
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnChatMemberCopyWith<$Res>? get profile {
if (_self.profile == null) {
return null;
}
return $SnChatMemberCopyWith<$Res>(_self.profile!, (value) {
return _then(_self.copyWith(profile: value));
});
}
} }
@@ -2649,10 +2635,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String identity, String name, DateTime joinedAt, String? accountId, SnChatMember? profile)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String identity, String name, DateTime joinedAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _CallParticipant() when $default != null: case _CallParticipant() when $default != null:
return $default(_that.identity,_that.name,_that.joinedAt,_that.accountId,_that.profile);case _: return $default(_that.identity,_that.name,_that.joinedAt);case _:
return orElse(); return orElse();
} }
@@ -2670,10 +2656,10 @@ return $default(_that.identity,_that.name,_that.joinedAt,_that.accountId,_that.p
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String identity, String name, DateTime joinedAt, String? accountId, SnChatMember? profile) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String identity, String name, DateTime joinedAt) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _CallParticipant(): case _CallParticipant():
return $default(_that.identity,_that.name,_that.joinedAt,_that.accountId,_that.profile);} return $default(_that.identity,_that.name,_that.joinedAt);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
/// ///
@@ -2687,10 +2673,10 @@ return $default(_that.identity,_that.name,_that.joinedAt,_that.accountId,_that.p
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String identity, String name, DateTime joinedAt, String? accountId, SnChatMember? profile)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String identity, String name, DateTime joinedAt)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _CallParticipant() when $default != null: case _CallParticipant() when $default != null:
return $default(_that.identity,_that.name,_that.joinedAt,_that.accountId,_that.profile);case _: return $default(_that.identity,_that.name,_that.joinedAt);case _:
return null; return null;
} }
@@ -2702,14 +2688,12 @@ return $default(_that.identity,_that.name,_that.joinedAt,_that.accountId,_that.p
@JsonSerializable() @JsonSerializable()
class _CallParticipant implements CallParticipant { class _CallParticipant implements CallParticipant {
const _CallParticipant({required this.identity, required this.name, required this.joinedAt, required this.accountId, required this.profile}); const _CallParticipant({required this.identity, required this.name, required this.joinedAt});
factory _CallParticipant.fromJson(Map<String, dynamic> json) => _$CallParticipantFromJson(json); factory _CallParticipant.fromJson(Map<String, dynamic> json) => _$CallParticipantFromJson(json);
@override final String identity; @override final String identity;
@override final String name; @override final String name;
@override final DateTime joinedAt; @override final DateTime joinedAt;
@override final String? accountId;
@override final SnChatMember? profile;
/// Create a copy of CallParticipant /// Create a copy of CallParticipant
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -2724,16 +2708,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallParticipant&&(identical(other.identity, identity) || other.identity == identity)&&(identical(other.name, name) || other.name == name)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.profile, profile) || other.profile == profile)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallParticipant&&(identical(other.identity, identity) || other.identity == identity)&&(identical(other.name, name) || other.name == name)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,identity,name,joinedAt,accountId,profile); int get hashCode => Object.hash(runtimeType,identity,name,joinedAt);
@override @override
String toString() { String toString() {
return 'CallParticipant(identity: $identity, name: $name, joinedAt: $joinedAt, accountId: $accountId, profile: $profile)'; return 'CallParticipant(identity: $identity, name: $name, joinedAt: $joinedAt)';
} }
@@ -2744,11 +2728,11 @@ abstract mixin class _$CallParticipantCopyWith<$Res> implements $CallParticipant
factory _$CallParticipantCopyWith(_CallParticipant value, $Res Function(_CallParticipant) _then) = __$CallParticipantCopyWithImpl; factory _$CallParticipantCopyWith(_CallParticipant value, $Res Function(_CallParticipant) _then) = __$CallParticipantCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String identity, String name, DateTime joinedAt, String? accountId, SnChatMember? profile String identity, String name, DateTime joinedAt
}); });
@override $SnChatMemberCopyWith<$Res>? get profile;
} }
/// @nodoc /// @nodoc
@@ -2761,30 +2745,16 @@ class __$CallParticipantCopyWithImpl<$Res>
/// Create a copy of CallParticipant /// Create a copy of CallParticipant
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? identity = null,Object? name = null,Object? joinedAt = null,Object? accountId = freezed,Object? profile = freezed,}) { @override @pragma('vm:prefer-inline') $Res call({Object? identity = null,Object? name = null,Object? joinedAt = null,}) {
return _then(_CallParticipant( return _then(_CallParticipant(
identity: null == identity ? _self.identity : identity // ignore: cast_nullable_to_non_nullable identity: null == identity ? _self.identity : identity // ignore: cast_nullable_to_non_nullable
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String,joinedAt: null == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable as String,joinedAt: null == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable
as DateTime,accountId: freezed == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable as DateTime,
as String?,profile: freezed == profile ? _self.profile : profile // ignore: cast_nullable_to_non_nullable
as SnChatMember?,
)); ));
} }
/// Create a copy of CallParticipant
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnChatMemberCopyWith<$Res>? get profile {
if (_self.profile == null) {
return null;
}
return $SnChatMemberCopyWith<$Res>(_self.profile!, (value) {
return _then(_self.copyWith(profile: value));
});
}
} }

View File

@@ -285,11 +285,6 @@ _CallParticipant _$CallParticipantFromJson(Map<String, dynamic> json) =>
identity: json['identity'] as String, identity: json['identity'] as String,
name: json['name'] as String, name: json['name'] as String,
joinedAt: DateTime.parse(json['joined_at'] as String), joinedAt: DateTime.parse(json['joined_at'] as String),
accountId: json['account_id'] as String?,
profile:
json['profile'] == null
? null
: SnChatMember.fromJson(json['profile'] as Map<String, dynamic>),
); );
Map<String, dynamic> _$CallParticipantToJson(_CallParticipant instance) => Map<String, dynamic> _$CallParticipantToJson(_CallParticipant instance) =>
@@ -297,8 +292,6 @@ Map<String, dynamic> _$CallParticipantToJson(_CallParticipant instance) =>
'identity': instance.identity, 'identity': instance.identity,
'name': instance.name, 'name': instance.name,
'joined_at': instance.joinedAt.toIso8601String(), 'joined_at': instance.joinedAt.toIso8601String(),
'account_id': instance.accountId,
'profile': instance.profile?.toJson(),
}; };
_SnRealtimeCall _$SnRealtimeCallFromJson(Map<String, dynamic> json) => _SnRealtimeCall _$SnRealtimeCallFromJson(Map<String, dynamic> json) =>

View File

@@ -1,13 +1,25 @@
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:island/models/publisher.dart';
part 'developer.freezed.dart'; part 'developer.freezed.dart';
part 'developer.g.dart'; part 'developer.g.dart';
@freezed
sealed class SnDeveloper with _$SnDeveloper {
const factory SnDeveloper({
required String id,
required String publisherId,
SnPublisher? publisher,
}) = _SnDeveloper;
factory SnDeveloper.fromJson(Map<String, dynamic> json) =>
_$SnDeveloperFromJson(json);
}
@freezed @freezed
sealed class DeveloperStats with _$DeveloperStats { sealed class DeveloperStats with _$DeveloperStats {
const factory DeveloperStats({ const factory DeveloperStats({@Default(0) int totalCustomApps}) =
@Default(0) int totalCustomApps, _DeveloperStats;
}) = _DeveloperStats;
factory DeveloperStats.fromJson(Map<String, dynamic> json) => factory DeveloperStats.fromJson(Map<String, dynamic> json) =>
_$DeveloperStatsFromJson(json); _$DeveloperStatsFromJson(json);

View File

@@ -12,6 +12,293 @@ part of 'developer.dart';
// dart format off // dart format off
T _$identity<T>(T value) => value; T _$identity<T>(T value) => value;
/// @nodoc
mixin _$SnDeveloper {
String get id; String get publisherId; SnPublisher? get publisher;
/// Create a copy of SnDeveloper
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$SnDeveloperCopyWith<SnDeveloper> get copyWith => _$SnDeveloperCopyWithImpl<SnDeveloper>(this as SnDeveloper, _$identity);
/// Serializes this SnDeveloper to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnDeveloper&&(identical(other.id, id) || other.id == id)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,publisherId,publisher);
@override
String toString() {
return 'SnDeveloper(id: $id, publisherId: $publisherId, publisher: $publisher)';
}
}
/// @nodoc
abstract mixin class $SnDeveloperCopyWith<$Res> {
factory $SnDeveloperCopyWith(SnDeveloper value, $Res Function(SnDeveloper) _then) = _$SnDeveloperCopyWithImpl;
@useResult
$Res call({
String id, String publisherId, SnPublisher? publisher
});
$SnPublisherCopyWith<$Res>? get publisher;
}
/// @nodoc
class _$SnDeveloperCopyWithImpl<$Res>
implements $SnDeveloperCopyWith<$Res> {
_$SnDeveloperCopyWithImpl(this._self, this._then);
final SnDeveloper _self;
final $Res Function(SnDeveloper) _then;
/// Create a copy of SnDeveloper
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? publisherId = null,Object? publisher = freezed,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,publisherId: null == 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?,
));
}
/// Create a copy of SnDeveloper
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnPublisherCopyWith<$Res>? get publisher {
if (_self.publisher == null) {
return null;
}
return $SnPublisherCopyWith<$Res>(_self.publisher!, (value) {
return _then(_self.copyWith(publisher: value));
});
}
}
/// Adds pattern-matching-related methods to [SnDeveloper].
extension SnDeveloperPatterns on SnDeveloper {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnDeveloper value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _SnDeveloper() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnDeveloper value) $default,){
final _that = this;
switch (_that) {
case _SnDeveloper():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnDeveloper value)? $default,){
final _that = this;
switch (_that) {
case _SnDeveloper() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String publisherId, SnPublisher? publisher)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SnDeveloper() when $default != null:
return $default(_that.id,_that.publisherId,_that.publisher);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String publisherId, SnPublisher? publisher) $default,) {final _that = this;
switch (_that) {
case _SnDeveloper():
return $default(_that.id,_that.publisherId,_that.publisher);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String publisherId, SnPublisher? publisher)? $default,) {final _that = this;
switch (_that) {
case _SnDeveloper() when $default != null:
return $default(_that.id,_that.publisherId,_that.publisher);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _SnDeveloper implements SnDeveloper {
const _SnDeveloper({required this.id, required this.publisherId, this.publisher});
factory _SnDeveloper.fromJson(Map<String, dynamic> json) => _$SnDeveloperFromJson(json);
@override final String id;
@override final String publisherId;
@override final SnPublisher? publisher;
/// Create a copy of SnDeveloper
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SnDeveloperCopyWith<_SnDeveloper> get copyWith => __$SnDeveloperCopyWithImpl<_SnDeveloper>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$SnDeveloperToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnDeveloper&&(identical(other.id, id) || other.id == id)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,publisherId,publisher);
@override
String toString() {
return 'SnDeveloper(id: $id, publisherId: $publisherId, publisher: $publisher)';
}
}
/// @nodoc
abstract mixin class _$SnDeveloperCopyWith<$Res> implements $SnDeveloperCopyWith<$Res> {
factory _$SnDeveloperCopyWith(_SnDeveloper value, $Res Function(_SnDeveloper) _then) = __$SnDeveloperCopyWithImpl;
@override @useResult
$Res call({
String id, String publisherId, SnPublisher? publisher
});
@override $SnPublisherCopyWith<$Res>? get publisher;
}
/// @nodoc
class __$SnDeveloperCopyWithImpl<$Res>
implements _$SnDeveloperCopyWith<$Res> {
__$SnDeveloperCopyWithImpl(this._self, this._then);
final _SnDeveloper _self;
final $Res Function(_SnDeveloper) _then;
/// Create a copy of SnDeveloper
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? publisherId = null,Object? publisher = freezed,}) {
return _then(_SnDeveloper(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,publisherId: null == 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?,
));
}
/// Create a copy of SnDeveloper
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnPublisherCopyWith<$Res>? get publisher {
if (_self.publisher == null) {
return null;
}
return $SnPublisherCopyWith<$Res>(_self.publisher!, (value) {
return _then(_self.copyWith(publisher: value));
});
}
}
/// @nodoc /// @nodoc
mixin _$DeveloperStats { mixin _$DeveloperStats {

View File

@@ -6,6 +6,22 @@ part of 'developer.dart';
// JsonSerializableGenerator // JsonSerializableGenerator
// ************************************************************************** // **************************************************************************
_SnDeveloper _$SnDeveloperFromJson(Map<String, dynamic> json) => _SnDeveloper(
id: json['id'] as String,
publisherId: json['publisher_id'] as String,
publisher:
json['publisher'] == null
? null
: SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
);
Map<String, dynamic> _$SnDeveloperToJson(_SnDeveloper instance) =>
<String, dynamic>{
'id': instance.id,
'publisher_id': instance.publisherId,
'publisher': instance.publisher?.toJson(),
};
_DeveloperStats _$DeveloperStatsFromJson(Map<String, dynamic> json) => _DeveloperStats _$DeveloperStatsFromJson(Map<String, dynamic> json) =>
_DeveloperStats( _DeveloperStats(
totalCustomApps: (json['total_custom_apps'] as num?)?.toInt() ?? 0, totalCustomApps: (json['total_custom_apps'] as num?)?.toInt() ?? 0,

View File

@@ -3,25 +3,6 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'embed.freezed.dart'; part 'embed.freezed.dart';
part 'embed.g.dart'; part 'embed.g.dart';
@freezed
sealed class SnEmbedLink with _$SnEmbedLink {
const factory SnEmbedLink({
@JsonKey(name: 'Type') required String type,
@JsonKey(name: 'Url') required String url,
@JsonKey(name: 'Title') required String title,
@JsonKey(name: 'Description') required String? description,
@JsonKey(name: 'ImageUrl') required String? imageUrl,
@JsonKey(name: 'FaviconUrl') required String faviconUrl,
@JsonKey(name: 'SiteName') required String siteName,
@JsonKey(name: 'ContentType') required String? contentType,
@JsonKey(name: 'Author') required String? author,
@JsonKey(name: 'PublishedDate') required DateTime? publishedDate,
}) = _SnEmbedLink;
factory SnEmbedLink.fromJson(Map<String, dynamic> json) =>
_$SnEmbedLinkFromJson(json);
}
@freezed @freezed
sealed class SnScrappedLink with _$SnScrappedLink { sealed class SnScrappedLink with _$SnScrappedLink {
const factory SnScrappedLink({ const factory SnScrappedLink({

View File

@@ -12,290 +12,6 @@ part of 'embed.dart';
// dart format off // dart format off
T _$identity<T>(T value) => value; T _$identity<T>(T value) => value;
/// @nodoc
mixin _$SnEmbedLink {
@JsonKey(name: 'Type') String get type;@JsonKey(name: 'Url') String get url;@JsonKey(name: 'Title') String get title;@JsonKey(name: 'Description') String? get description;@JsonKey(name: 'ImageUrl') String? get imageUrl;@JsonKey(name: 'FaviconUrl') String get faviconUrl;@JsonKey(name: 'SiteName') String get siteName;@JsonKey(name: 'ContentType') String? get contentType;@JsonKey(name: 'Author') String? get author;@JsonKey(name: 'PublishedDate') DateTime? get publishedDate;
/// Create a copy of SnEmbedLink
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$SnEmbedLinkCopyWith<SnEmbedLink> get copyWith => _$SnEmbedLinkCopyWithImpl<SnEmbedLink>(this as SnEmbedLink, _$identity);
/// Serializes this SnEmbedLink to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnEmbedLink&&(identical(other.type, type) || other.type == type)&&(identical(other.url, url) || other.url == url)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.imageUrl, imageUrl) || other.imageUrl == imageUrl)&&(identical(other.faviconUrl, faviconUrl) || other.faviconUrl == faviconUrl)&&(identical(other.siteName, siteName) || other.siteName == siteName)&&(identical(other.contentType, contentType) || other.contentType == contentType)&&(identical(other.author, author) || other.author == author)&&(identical(other.publishedDate, publishedDate) || other.publishedDate == publishedDate));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,type,url,title,description,imageUrl,faviconUrl,siteName,contentType,author,publishedDate);
@override
String toString() {
return 'SnEmbedLink(type: $type, url: $url, title: $title, description: $description, imageUrl: $imageUrl, faviconUrl: $faviconUrl, siteName: $siteName, contentType: $contentType, author: $author, publishedDate: $publishedDate)';
}
}
/// @nodoc
abstract mixin class $SnEmbedLinkCopyWith<$Res> {
factory $SnEmbedLinkCopyWith(SnEmbedLink value, $Res Function(SnEmbedLink) _then) = _$SnEmbedLinkCopyWithImpl;
@useResult
$Res call({
@JsonKey(name: 'Type') String type,@JsonKey(name: 'Url') String url,@JsonKey(name: 'Title') String title,@JsonKey(name: 'Description') String? description,@JsonKey(name: 'ImageUrl') String? imageUrl,@JsonKey(name: 'FaviconUrl') String faviconUrl,@JsonKey(name: 'SiteName') String siteName,@JsonKey(name: 'ContentType') String? contentType,@JsonKey(name: 'Author') String? author,@JsonKey(name: 'PublishedDate') DateTime? publishedDate
});
}
/// @nodoc
class _$SnEmbedLinkCopyWithImpl<$Res>
implements $SnEmbedLinkCopyWith<$Res> {
_$SnEmbedLinkCopyWithImpl(this._self, this._then);
final SnEmbedLink _self;
final $Res Function(SnEmbedLink) _then;
/// Create a copy of SnEmbedLink
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? url = null,Object? title = null,Object? description = freezed,Object? imageUrl = freezed,Object? faviconUrl = null,Object? siteName = null,Object? contentType = freezed,Object? author = freezed,Object? publishedDate = freezed,}) {
return _then(_self.copyWith(
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
as String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable
as String,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,imageUrl: freezed == imageUrl ? _self.imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable
as String?,faviconUrl: null == faviconUrl ? _self.faviconUrl : faviconUrl // ignore: cast_nullable_to_non_nullable
as String,siteName: null == siteName ? _self.siteName : siteName // ignore: cast_nullable_to_non_nullable
as String,contentType: freezed == contentType ? _self.contentType : contentType // ignore: cast_nullable_to_non_nullable
as String?,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
as String?,publishedDate: freezed == publishedDate ? _self.publishedDate : publishedDate // ignore: cast_nullable_to_non_nullable
as DateTime?,
));
}
}
/// Adds pattern-matching-related methods to [SnEmbedLink].
extension SnEmbedLinkPatterns on SnEmbedLink {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnEmbedLink value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _SnEmbedLink() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnEmbedLink value) $default,){
final _that = this;
switch (_that) {
case _SnEmbedLink():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnEmbedLink value)? $default,){
final _that = this;
switch (_that) {
case _SnEmbedLink() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'Type') String type, @JsonKey(name: 'Url') String url, @JsonKey(name: 'Title') String title, @JsonKey(name: 'Description') String? description, @JsonKey(name: 'ImageUrl') String? imageUrl, @JsonKey(name: 'FaviconUrl') String faviconUrl, @JsonKey(name: 'SiteName') String siteName, @JsonKey(name: 'ContentType') String? contentType, @JsonKey(name: 'Author') String? author, @JsonKey(name: 'PublishedDate') DateTime? publishedDate)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SnEmbedLink() when $default != null:
return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUrl,_that.faviconUrl,_that.siteName,_that.contentType,_that.author,_that.publishedDate);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'Type') String type, @JsonKey(name: 'Url') String url, @JsonKey(name: 'Title') String title, @JsonKey(name: 'Description') String? description, @JsonKey(name: 'ImageUrl') String? imageUrl, @JsonKey(name: 'FaviconUrl') String faviconUrl, @JsonKey(name: 'SiteName') String siteName, @JsonKey(name: 'ContentType') String? contentType, @JsonKey(name: 'Author') String? author, @JsonKey(name: 'PublishedDate') DateTime? publishedDate) $default,) {final _that = this;
switch (_that) {
case _SnEmbedLink():
return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUrl,_that.faviconUrl,_that.siteName,_that.contentType,_that.author,_that.publishedDate);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'Type') String type, @JsonKey(name: 'Url') String url, @JsonKey(name: 'Title') String title, @JsonKey(name: 'Description') String? description, @JsonKey(name: 'ImageUrl') String? imageUrl, @JsonKey(name: 'FaviconUrl') String faviconUrl, @JsonKey(name: 'SiteName') String siteName, @JsonKey(name: 'ContentType') String? contentType, @JsonKey(name: 'Author') String? author, @JsonKey(name: 'PublishedDate') DateTime? publishedDate)? $default,) {final _that = this;
switch (_that) {
case _SnEmbedLink() when $default != null:
return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUrl,_that.faviconUrl,_that.siteName,_that.contentType,_that.author,_that.publishedDate);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _SnEmbedLink implements SnEmbedLink {
const _SnEmbedLink({@JsonKey(name: 'Type') required this.type, @JsonKey(name: 'Url') required this.url, @JsonKey(name: 'Title') required this.title, @JsonKey(name: 'Description') required this.description, @JsonKey(name: 'ImageUrl') required this.imageUrl, @JsonKey(name: 'FaviconUrl') required this.faviconUrl, @JsonKey(name: 'SiteName') required this.siteName, @JsonKey(name: 'ContentType') required this.contentType, @JsonKey(name: 'Author') required this.author, @JsonKey(name: 'PublishedDate') required this.publishedDate});
factory _SnEmbedLink.fromJson(Map<String, dynamic> json) => _$SnEmbedLinkFromJson(json);
@override@JsonKey(name: 'Type') final String type;
@override@JsonKey(name: 'Url') final String url;
@override@JsonKey(name: 'Title') final String title;
@override@JsonKey(name: 'Description') final String? description;
@override@JsonKey(name: 'ImageUrl') final String? imageUrl;
@override@JsonKey(name: 'FaviconUrl') final String faviconUrl;
@override@JsonKey(name: 'SiteName') final String siteName;
@override@JsonKey(name: 'ContentType') final String? contentType;
@override@JsonKey(name: 'Author') final String? author;
@override@JsonKey(name: 'PublishedDate') final DateTime? publishedDate;
/// Create a copy of SnEmbedLink
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SnEmbedLinkCopyWith<_SnEmbedLink> get copyWith => __$SnEmbedLinkCopyWithImpl<_SnEmbedLink>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$SnEmbedLinkToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnEmbedLink&&(identical(other.type, type) || other.type == type)&&(identical(other.url, url) || other.url == url)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.imageUrl, imageUrl) || other.imageUrl == imageUrl)&&(identical(other.faviconUrl, faviconUrl) || other.faviconUrl == faviconUrl)&&(identical(other.siteName, siteName) || other.siteName == siteName)&&(identical(other.contentType, contentType) || other.contentType == contentType)&&(identical(other.author, author) || other.author == author)&&(identical(other.publishedDate, publishedDate) || other.publishedDate == publishedDate));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,type,url,title,description,imageUrl,faviconUrl,siteName,contentType,author,publishedDate);
@override
String toString() {
return 'SnEmbedLink(type: $type, url: $url, title: $title, description: $description, imageUrl: $imageUrl, faviconUrl: $faviconUrl, siteName: $siteName, contentType: $contentType, author: $author, publishedDate: $publishedDate)';
}
}
/// @nodoc
abstract mixin class _$SnEmbedLinkCopyWith<$Res> implements $SnEmbedLinkCopyWith<$Res> {
factory _$SnEmbedLinkCopyWith(_SnEmbedLink value, $Res Function(_SnEmbedLink) _then) = __$SnEmbedLinkCopyWithImpl;
@override @useResult
$Res call({
@JsonKey(name: 'Type') String type,@JsonKey(name: 'Url') String url,@JsonKey(name: 'Title') String title,@JsonKey(name: 'Description') String? description,@JsonKey(name: 'ImageUrl') String? imageUrl,@JsonKey(name: 'FaviconUrl') String faviconUrl,@JsonKey(name: 'SiteName') String siteName,@JsonKey(name: 'ContentType') String? contentType,@JsonKey(name: 'Author') String? author,@JsonKey(name: 'PublishedDate') DateTime? publishedDate
});
}
/// @nodoc
class __$SnEmbedLinkCopyWithImpl<$Res>
implements _$SnEmbedLinkCopyWith<$Res> {
__$SnEmbedLinkCopyWithImpl(this._self, this._then);
final _SnEmbedLink _self;
final $Res Function(_SnEmbedLink) _then;
/// Create a copy of SnEmbedLink
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? url = null,Object? title = null,Object? description = freezed,Object? imageUrl = freezed,Object? faviconUrl = null,Object? siteName = null,Object? contentType = freezed,Object? author = freezed,Object? publishedDate = freezed,}) {
return _then(_SnEmbedLink(
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
as String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable
as String,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,imageUrl: freezed == imageUrl ? _self.imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable
as String?,faviconUrl: null == faviconUrl ? _self.faviconUrl : faviconUrl // ignore: cast_nullable_to_non_nullable
as String,siteName: null == siteName ? _self.siteName : siteName // ignore: cast_nullable_to_non_nullable
as String,contentType: freezed == contentType ? _self.contentType : contentType // ignore: cast_nullable_to_non_nullable
as String?,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
as String?,publishedDate: freezed == publishedDate ? _self.publishedDate : publishedDate // ignore: cast_nullable_to_non_nullable
as DateTime?,
));
}
}
/// @nodoc /// @nodoc
mixin _$SnScrappedLink { mixin _$SnScrappedLink {

View File

@@ -6,36 +6,6 @@ part of 'embed.dart';
// JsonSerializableGenerator // JsonSerializableGenerator
// ************************************************************************** // **************************************************************************
_SnEmbedLink _$SnEmbedLinkFromJson(Map<String, dynamic> json) => _SnEmbedLink(
type: json['Type'] as String,
url: json['Url'] as String,
title: json['Title'] as String,
description: json['Description'] as String?,
imageUrl: json['ImageUrl'] as String?,
faviconUrl: json['FaviconUrl'] as String,
siteName: json['SiteName'] as String,
contentType: json['ContentType'] as String?,
author: json['Author'] as String?,
publishedDate:
json['PublishedDate'] == null
? null
: DateTime.parse(json['PublishedDate'] as String),
);
Map<String, dynamic> _$SnEmbedLinkToJson(_SnEmbedLink instance) =>
<String, dynamic>{
'Type': instance.type,
'Url': instance.url,
'Title': instance.title,
'Description': instance.description,
'ImageUrl': instance.imageUrl,
'FaviconUrl': instance.faviconUrl,
'SiteName': instance.siteName,
'ContentType': instance.contentType,
'Author': instance.author,
'PublishedDate': instance.publishedDate?.toIso8601String(),
};
_SnScrappedLink _$SnScrappedLinkFromJson(Map<String, dynamic> json) => _SnScrappedLink _$SnScrappedLinkFromJson(Map<String, dynamic> json) =>
_SnScrappedLink( _SnScrappedLink(
type: json['type'] as String, type: json['type'] as String,

View File

@@ -12,6 +12,7 @@ sealed class UniversalFile with _$UniversalFile {
const factory UniversalFile({ const factory UniversalFile({
required dynamic data, required dynamic data,
required UniversalFileType type, required UniversalFileType type,
@Default(false) bool isLink,
}) = _UniversalFile; }) = _UniversalFile;
factory UniversalFile.fromJson(Map<String, dynamic> json) => factory UniversalFile.fromJson(Map<String, dynamic> json) =>
@@ -41,6 +42,7 @@ sealed class SnCloudFile with _$SnCloudFile {
required String? description, required String? description,
required Map<String, dynamic>? fileMeta, required Map<String, dynamic>? fileMeta,
required Map<String, dynamic>? userMeta, required Map<String, dynamic>? userMeta,
@Default([]) List<int> sensitiveMarks,
required String? mimeType, required String? mimeType,
required String? hash, required String? hash,
required int size, required int size,

View File

@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$UniversalFile { mixin _$UniversalFile {
dynamic get data; UniversalFileType get type; dynamic get data; UniversalFileType get type; bool get isLink;
/// Create a copy of UniversalFile /// Create a copy of UniversalFile
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -28,16 +28,16 @@ $UniversalFileCopyWith<UniversalFile> get copyWith => _$UniversalFileCopyWithImp
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is UniversalFile&&const DeepCollectionEquality().equals(other.data, data)&&(identical(other.type, type) || other.type == type)); return identical(this, other) || (other.runtimeType == runtimeType&&other is UniversalFile&&const DeepCollectionEquality().equals(other.data, data)&&(identical(other.type, type) || other.type == type)&&(identical(other.isLink, isLink) || other.isLink == isLink));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(data),type); int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(data),type,isLink);
@override @override
String toString() { String toString() {
return 'UniversalFile(data: $data, type: $type)'; return 'UniversalFile(data: $data, type: $type, isLink: $isLink)';
} }
@@ -48,7 +48,7 @@ abstract mixin class $UniversalFileCopyWith<$Res> {
factory $UniversalFileCopyWith(UniversalFile value, $Res Function(UniversalFile) _then) = _$UniversalFileCopyWithImpl; factory $UniversalFileCopyWith(UniversalFile value, $Res Function(UniversalFile) _then) = _$UniversalFileCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
dynamic data, UniversalFileType type dynamic data, UniversalFileType type, bool isLink
}); });
@@ -65,11 +65,12 @@ class _$UniversalFileCopyWithImpl<$Res>
/// Create a copy of UniversalFile /// Create a copy of UniversalFile
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? data = freezed,Object? type = null,}) { @pragma('vm:prefer-inline') @override $Res call({Object? data = freezed,Object? type = null,Object? isLink = null,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable
as dynamic,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable as dynamic,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
as UniversalFileType, as UniversalFileType,isLink: null == isLink ? _self.isLink : isLink // ignore: cast_nullable_to_non_nullable
as bool,
)); ));
} }
@@ -151,10 +152,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( dynamic data, UniversalFileType type)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( dynamic data, UniversalFileType type, bool isLink)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _UniversalFile() when $default != null: case _UniversalFile() when $default != null:
return $default(_that.data,_that.type);case _: return $default(_that.data,_that.type,_that.isLink);case _:
return orElse(); return orElse();
} }
@@ -172,10 +173,10 @@ return $default(_that.data,_that.type);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( dynamic data, UniversalFileType type) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( dynamic data, UniversalFileType type, bool isLink) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _UniversalFile(): case _UniversalFile():
return $default(_that.data,_that.type);} return $default(_that.data,_that.type,_that.isLink);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
/// ///
@@ -189,10 +190,10 @@ return $default(_that.data,_that.type);}
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( dynamic data, UniversalFileType type)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( dynamic data, UniversalFileType type, bool isLink)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _UniversalFile() when $default != null: case _UniversalFile() when $default != null:
return $default(_that.data,_that.type);case _: return $default(_that.data,_that.type,_that.isLink);case _:
return null; return null;
} }
@@ -204,11 +205,12 @@ return $default(_that.data,_that.type);case _:
@JsonSerializable() @JsonSerializable()
class _UniversalFile extends UniversalFile { class _UniversalFile extends UniversalFile {
const _UniversalFile({required this.data, required this.type}): super._(); const _UniversalFile({required this.data, required this.type, this.isLink = false}): super._();
factory _UniversalFile.fromJson(Map<String, dynamic> json) => _$UniversalFileFromJson(json); factory _UniversalFile.fromJson(Map<String, dynamic> json) => _$UniversalFileFromJson(json);
@override final dynamic data; @override final dynamic data;
@override final UniversalFileType type; @override final UniversalFileType type;
@override@JsonKey() final bool isLink;
/// Create a copy of UniversalFile /// Create a copy of UniversalFile
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -223,16 +225,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _UniversalFile&&const DeepCollectionEquality().equals(other.data, data)&&(identical(other.type, type) || other.type == type)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _UniversalFile&&const DeepCollectionEquality().equals(other.data, data)&&(identical(other.type, type) || other.type == type)&&(identical(other.isLink, isLink) || other.isLink == isLink));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(data),type); int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(data),type,isLink);
@override @override
String toString() { String toString() {
return 'UniversalFile(data: $data, type: $type)'; return 'UniversalFile(data: $data, type: $type, isLink: $isLink)';
} }
@@ -243,7 +245,7 @@ abstract mixin class _$UniversalFileCopyWith<$Res> implements $UniversalFileCopy
factory _$UniversalFileCopyWith(_UniversalFile value, $Res Function(_UniversalFile) _then) = __$UniversalFileCopyWithImpl; factory _$UniversalFileCopyWith(_UniversalFile value, $Res Function(_UniversalFile) _then) = __$UniversalFileCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
dynamic data, UniversalFileType type dynamic data, UniversalFileType type, bool isLink
}); });
@@ -260,11 +262,12 @@ class __$UniversalFileCopyWithImpl<$Res>
/// Create a copy of UniversalFile /// Create a copy of UniversalFile
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? data = freezed,Object? type = null,}) { @override @pragma('vm:prefer-inline') $Res call({Object? data = freezed,Object? type = null,Object? isLink = null,}) {
return _then(_UniversalFile( return _then(_UniversalFile(
data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable
as dynamic,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable as dynamic,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
as UniversalFileType, as UniversalFileType,isLink: null == isLink ? _self.isLink : isLink // ignore: cast_nullable_to_non_nullable
as bool,
)); ));
} }
@@ -275,7 +278,7 @@ as UniversalFileType,
/// @nodoc /// @nodoc
mixin _$SnCloudFile { mixin _$SnCloudFile {
String get id; String get name; String? get description; Map<String, dynamic>? get fileMeta; Map<String, dynamic>? get userMeta; 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; 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;
/// Create a copy of SnCloudFile /// Create a copy of SnCloudFile
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -288,16 +291,16 @@ $SnCloudFileCopyWith<SnCloudFile> get copyWith => _$SnCloudFileCopyWithImpl<SnCl
@override @override
bool operator ==(Object other) { 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.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)&&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));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,id,name,description,const DeepCollectionEquality().hash(fileMeta),const DeepCollectionEquality().hash(userMeta),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),const DeepCollectionEquality().hash(sensitiveMarks),mimeType,hash,size,uploadedAt,uploadedTo,createdAt,updatedAt,deletedAt);
@override @override
String toString() { String toString() {
return 'SnCloudFile(id: $id, name: $name, description: $description, fileMeta: $fileMeta, userMeta: $userMeta, 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, sensitiveMarks: $sensitiveMarks, mimeType: $mimeType, hash: $hash, size: $size, uploadedAt: $uploadedAt, uploadedTo: $uploadedTo, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
} }
@@ -308,7 +311,7 @@ abstract mixin class $SnCloudFileCopyWith<$Res> {
factory $SnCloudFileCopyWith(SnCloudFile value, $Res Function(SnCloudFile) _then) = _$SnCloudFileCopyWithImpl; factory $SnCloudFileCopyWith(SnCloudFile value, $Res Function(SnCloudFile) _then) = _$SnCloudFileCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String id, String name, String? description, Map<String, dynamic>? fileMeta, Map<String, dynamic>? userMeta, 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, List<int> sensitiveMarks, String? mimeType, String? hash, int size, DateTime? uploadedAt, String? uploadedTo, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
}); });
@@ -325,14 +328,15 @@ class _$SnCloudFileCopyWithImpl<$Res>
/// Create a copy of SnCloudFile /// Create a copy of SnCloudFile
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? description = freezed,Object? fileMeta = freezed,Object? userMeta = freezed,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? 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,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,fileMeta: freezed == fileMeta ? _self.fileMeta : fileMeta // ignore: cast_nullable_to_non_nullable as String?,fileMeta: freezed == fileMeta ? _self.fileMeta : fileMeta // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,userMeta: freezed == userMeta ? _self.userMeta : userMeta // ignore: cast_nullable_to_non_nullable as Map<String, dynamic>?,userMeta: freezed == userMeta ? _self.userMeta : userMeta // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,mimeType: freezed == mimeType ? _self.mimeType : mimeType // ignore: cast_nullable_to_non_nullable as Map<String, dynamic>?,sensitiveMarks: null == sensitiveMarks ? _self.sensitiveMarks : sensitiveMarks // ignore: cast_nullable_to_non_nullable
as List<int>,mimeType: freezed == mimeType ? _self.mimeType : mimeType // ignore: cast_nullable_to_non_nullable
as String?,hash: freezed == hash ? _self.hash : hash // ignore: cast_nullable_to_non_nullable as String?,hash: freezed == hash ? _self.hash : hash // ignore: cast_nullable_to_non_nullable
as String?,size: null == size ? _self.size : size // ignore: cast_nullable_to_non_nullable as String?,size: null == size ? _self.size : size // ignore: cast_nullable_to_non_nullable
as int,uploadedAt: freezed == uploadedAt ? _self.uploadedAt : uploadedAt // ignore: cast_nullable_to_non_nullable as int,uploadedAt: freezed == uploadedAt ? _self.uploadedAt : uploadedAt // ignore: cast_nullable_to_non_nullable
@@ -422,10 +426,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, 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, 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;
switch (_that) { switch (_that) {
case _SnCloudFile() when $default != null: case _SnCloudFile() when $default != null:
return $default(_that.id,_that.name,_that.description,_that.fileMeta,_that.userMeta,_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.sensitiveMarks,_that.mimeType,_that.hash,_that.size,_that.uploadedAt,_that.uploadedTo,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
return orElse(); return orElse();
} }
@@ -443,10 +447,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, 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, List<int> sensitiveMarks, String? mimeType, String? hash, int size, DateTime? uploadedAt, String? uploadedTo, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnCloudFile(): case _SnCloudFile():
return $default(_that.id,_that.name,_that.description,_that.fileMeta,_that.userMeta,_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.sensitiveMarks,_that.mimeType,_that.hash,_that.size,_that.uploadedAt,_that.uploadedTo,_that.createdAt,_that.updatedAt,_that.deletedAt);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
/// ///
@@ -460,10 +464,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, 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, List<int> sensitiveMarks, String? mimeType, String? hash, int size, DateTime? uploadedAt, String? uploadedTo, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnCloudFile() when $default != null: case _SnCloudFile() when $default != null:
return $default(_that.id,_that.name,_that.description,_that.fileMeta,_that.userMeta,_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.sensitiveMarks,_that.mimeType,_that.hash,_that.size,_that.uploadedAt,_that.uploadedTo,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
return null; return null;
} }
@@ -475,7 +479,7 @@ return $default(_that.id,_that.name,_that.description,_that.fileMeta,_that.userM
@JsonSerializable() @JsonSerializable()
class _SnCloudFile implements SnCloudFile { 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.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; const _SnCloudFile({required this.id, required this.name, required this.description, required final Map<String, dynamic>? fileMeta, required final Map<String, dynamic>? userMeta, 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;
factory _SnCloudFile.fromJson(Map<String, dynamic> json) => _$SnCloudFileFromJson(json); factory _SnCloudFile.fromJson(Map<String, dynamic> json) => _$SnCloudFileFromJson(json);
@override final String id; @override final String id;
@@ -499,6 +503,13 @@ class _SnCloudFile implements SnCloudFile {
return EqualUnmodifiableMapView(value); return EqualUnmodifiableMapView(value);
} }
final List<int> _sensitiveMarks;
@override@JsonKey() List<int> get sensitiveMarks {
if (_sensitiveMarks is EqualUnmodifiableListView) return _sensitiveMarks;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_sensitiveMarks);
}
@override final String? mimeType; @override final String? mimeType;
@override final String? hash; @override final String? hash;
@override final int size; @override final int size;
@@ -521,16 +532,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { 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.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)&&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));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,id,name,description,const DeepCollectionEquality().hash(_fileMeta),const DeepCollectionEquality().hash(_userMeta),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),const DeepCollectionEquality().hash(_sensitiveMarks),mimeType,hash,size,uploadedAt,uploadedTo,createdAt,updatedAt,deletedAt);
@override @override
String toString() { String toString() {
return 'SnCloudFile(id: $id, name: $name, description: $description, fileMeta: $fileMeta, userMeta: $userMeta, 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, sensitiveMarks: $sensitiveMarks, mimeType: $mimeType, hash: $hash, size: $size, uploadedAt: $uploadedAt, uploadedTo: $uploadedTo, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
} }
@@ -541,7 +552,7 @@ abstract mixin class _$SnCloudFileCopyWith<$Res> implements $SnCloudFileCopyWith
factory _$SnCloudFileCopyWith(_SnCloudFile value, $Res Function(_SnCloudFile) _then) = __$SnCloudFileCopyWithImpl; factory _$SnCloudFileCopyWith(_SnCloudFile value, $Res Function(_SnCloudFile) _then) = __$SnCloudFileCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String id, String name, String? description, Map<String, dynamic>? fileMeta, Map<String, dynamic>? userMeta, 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, List<int> sensitiveMarks, String? mimeType, String? hash, int size, DateTime? uploadedAt, String? uploadedTo, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
}); });
@@ -558,14 +569,15 @@ class __$SnCloudFileCopyWithImpl<$Res>
/// Create a copy of SnCloudFile /// Create a copy of SnCloudFile
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? description = freezed,Object? fileMeta = freezed,Object? userMeta = freezed,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? 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,}) {
return _then(_SnCloudFile( return _then(_SnCloudFile(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String?,fileMeta: freezed == fileMeta ? _self._fileMeta : fileMeta // ignore: cast_nullable_to_non_nullable as String?,fileMeta: freezed == fileMeta ? _self._fileMeta : fileMeta // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,userMeta: freezed == userMeta ? _self._userMeta : userMeta // ignore: cast_nullable_to_non_nullable as Map<String, dynamic>?,userMeta: freezed == userMeta ? _self._userMeta : userMeta // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,mimeType: freezed == mimeType ? _self.mimeType : mimeType // ignore: cast_nullable_to_non_nullable as Map<String, dynamic>?,sensitiveMarks: null == sensitiveMarks ? _self._sensitiveMarks : sensitiveMarks // ignore: cast_nullable_to_non_nullable
as List<int>,mimeType: freezed == mimeType ? _self.mimeType : mimeType // ignore: cast_nullable_to_non_nullable
as String?,hash: freezed == hash ? _self.hash : hash // ignore: cast_nullable_to_non_nullable as String?,hash: freezed == hash ? _self.hash : hash // ignore: cast_nullable_to_non_nullable
as String?,size: null == size ? _self.size : size // ignore: cast_nullable_to_non_nullable as String?,size: null == size ? _self.size : size // ignore: cast_nullable_to_non_nullable
as int,uploadedAt: freezed == uploadedAt ? _self.uploadedAt : uploadedAt // ignore: cast_nullable_to_non_nullable as int,uploadedAt: freezed == uploadedAt ? _self.uploadedAt : uploadedAt // ignore: cast_nullable_to_non_nullable

View File

@@ -10,12 +10,14 @@ _UniversalFile _$UniversalFileFromJson(Map<String, dynamic> json) =>
_UniversalFile( _UniversalFile(
data: json['data'], data: json['data'],
type: $enumDecode(_$UniversalFileTypeEnumMap, json['type']), type: $enumDecode(_$UniversalFileTypeEnumMap, json['type']),
isLink: json['is_link'] as bool? ?? false,
); );
Map<String, dynamic> _$UniversalFileToJson(_UniversalFile instance) => Map<String, dynamic> _$UniversalFileToJson(_UniversalFile instance) =>
<String, dynamic>{ <String, dynamic>{
'data': instance.data, 'data': instance.data,
'type': _$UniversalFileTypeEnumMap[instance.type]!, 'type': _$UniversalFileTypeEnumMap[instance.type]!,
'is_link': instance.isLink,
}; };
const _$UniversalFileTypeEnumMap = { const _$UniversalFileTypeEnumMap = {
@@ -31,6 +33,11 @@ _SnCloudFile _$SnCloudFileFromJson(Map<String, dynamic> json) => _SnCloudFile(
description: json['description'] as String?, description: json['description'] as String?,
fileMeta: json['file_meta'] as Map<String, dynamic>?, fileMeta: json['file_meta'] as Map<String, dynamic>?,
userMeta: json['user_meta'] as Map<String, dynamic>?, userMeta: json['user_meta'] as Map<String, dynamic>?,
sensitiveMarks:
(json['sensitive_marks'] as List<dynamic>?)
?.map((e) => (e as num).toInt())
.toList() ??
const [],
mimeType: json['mime_type'] as String?, mimeType: json['mime_type'] as String?,
hash: json['hash'] as String?, hash: json['hash'] as String?,
size: (json['size'] as num).toInt(), size: (json['size'] as num).toInt(),
@@ -54,6 +61,7 @@ Map<String, dynamic> _$SnCloudFileToJson(_SnCloudFile instance) =>
'description': instance.description, 'description': instance.description,
'file_meta': instance.fileMeta, 'file_meta': instance.fileMeta,
'user_meta': instance.userMeta, 'user_meta': instance.userMeta,
'sensitive_marks': instance.sensitiveMarks,
'mime_type': instance.mimeType, 'mime_type': instance.mimeType,
'hash': instance.hash, 'hash': instance.hash,
'size': instance.size, 'size': instance.size,

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

@@ -0,0 +1,108 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:island/models/publisher.dart';
part 'poll.freezed.dart';
part 'poll.g.dart';
@freezed
sealed class SnPollWithStats with _$SnPollWithStats {
const factory SnPollWithStats({
required Map<String, dynamic>? userAnswer,
required Map<String, dynamic> stats,
required String id,
required List<SnPollQuestion> questions,
String? title,
String? description,
DateTime? endedAt,
required String publisherId,
required DateTime createdAt,
required DateTime updatedAt,
DateTime? deletedAt,
}) = _SnPollWithStats;
factory SnPollWithStats.fromJson(Map<String, dynamic> json) =>
_$SnPollWithStatsFromJson(json);
}
@freezed
sealed class SnPoll with _$SnPoll {
const factory SnPoll({
required String id,
required List<SnPollQuestion> questions,
String? title,
String? description,
DateTime? endedAt,
required String publisherId,
SnPublisher? publisher,
// ModelBase fields
required DateTime createdAt,
required DateTime updatedAt,
DateTime? deletedAt,
}) = _SnPoll;
factory SnPoll.fromJson(Map<String, dynamic> json) => _$SnPollFromJson(json);
}
@freezed
sealed class SnPollQuestion with _$SnPollQuestion {
const factory SnPollQuestion({
required String id,
required SnPollQuestionType type,
List<SnPollOption>? options,
required String title,
String? description,
required int order,
required bool isRequired,
}) = _SnPollQuestion;
factory SnPollQuestion.fromJson(Map<String, dynamic> json) =>
_$SnPollQuestionFromJson(json);
}
@freezed
sealed class SnPollOption with _$SnPollOption {
const factory SnPollOption({
required String id,
required String label,
String? description,
required int order,
}) = _SnPollOption;
factory SnPollOption.fromJson(Map<String, dynamic> json) =>
_$SnPollOptionFromJson(json);
}
enum SnPollQuestionType {
@JsonValue(0)
singleChoice,
@JsonValue(1)
multipleChoice,
@JsonValue(2)
yesNo,
@JsonValue(3)
rating,
@JsonValue(4)
freeText,
}
@freezed
sealed class SnPollAnswer with _$SnPollAnswer {
const factory SnPollAnswer({
required String id,
required Map<String, dynamic> answer,
required String accountId,
required String pollId,
required DateTime createdAt,
required DateTime updatedAt,
required DateTime? deletedAt,
}) = _SnPollAnswer;
factory SnPollAnswer.fromJson(Map<String, dynamic> json) =>
_$SnPollAnswerFromJson(json);
}

1467
lib/models/poll.freezed.dart Normal file

File diff suppressed because it is too large Load Diff

158
lib/models/poll.g.dart Normal file
View File

@@ -0,0 +1,158 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'poll.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_SnPollWithStats _$SnPollWithStatsFromJson(Map<String, dynamic> json) =>
_SnPollWithStats(
userAnswer: json['user_answer'] as Map<String, dynamic>?,
stats: json['stats'] as Map<String, dynamic>,
id: json['id'] as String,
questions:
(json['questions'] as List<dynamic>)
.map((e) => SnPollQuestion.fromJson(e as Map<String, dynamic>))
.toList(),
title: json['title'] as String?,
description: json['description'] as String?,
endedAt:
json['ended_at'] == null
? null
: DateTime.parse(json['ended_at'] as String),
publisherId: json['publisher_id'] as String,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt:
json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
);
Map<String, dynamic> _$SnPollWithStatsToJson(_SnPollWithStats instance) =>
<String, dynamic>{
'user_answer': instance.userAnswer,
'stats': instance.stats,
'id': instance.id,
'questions': instance.questions.map((e) => e.toJson()).toList(),
'title': instance.title,
'description': instance.description,
'ended_at': instance.endedAt?.toIso8601String(),
'publisher_id': instance.publisherId,
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
};
_SnPoll _$SnPollFromJson(Map<String, dynamic> json) => _SnPoll(
id: json['id'] as String,
questions:
(json['questions'] as List<dynamic>)
.map((e) => SnPollQuestion.fromJson(e as Map<String, dynamic>))
.toList(),
title: json['title'] as String?,
description: json['description'] as String?,
endedAt:
json['ended_at'] == null
? null
: DateTime.parse(json['ended_at'] as String),
publisherId: json['publisher_id'] as String,
publisher:
json['publisher'] == null
? null
: SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt:
json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
);
Map<String, dynamic> _$SnPollToJson(_SnPoll instance) => <String, dynamic>{
'id': instance.id,
'questions': instance.questions.map((e) => e.toJson()).toList(),
'title': instance.title,
'description': instance.description,
'ended_at': instance.endedAt?.toIso8601String(),
'publisher_id': instance.publisherId,
'publisher': instance.publisher?.toJson(),
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
};
_SnPollQuestion _$SnPollQuestionFromJson(Map<String, dynamic> json) =>
_SnPollQuestion(
id: json['id'] as String,
type: $enumDecode(_$SnPollQuestionTypeEnumMap, json['type']),
options:
(json['options'] as List<dynamic>?)
?.map((e) => SnPollOption.fromJson(e as Map<String, dynamic>))
.toList(),
title: json['title'] as String,
description: json['description'] as String?,
order: (json['order'] as num).toInt(),
isRequired: json['is_required'] as bool,
);
Map<String, dynamic> _$SnPollQuestionToJson(_SnPollQuestion instance) =>
<String, dynamic>{
'id': instance.id,
'type': _$SnPollQuestionTypeEnumMap[instance.type]!,
'options': instance.options?.map((e) => e.toJson()).toList(),
'title': instance.title,
'description': instance.description,
'order': instance.order,
'is_required': instance.isRequired,
};
const _$SnPollQuestionTypeEnumMap = {
SnPollQuestionType.singleChoice: 0,
SnPollQuestionType.multipleChoice: 1,
SnPollQuestionType.yesNo: 2,
SnPollQuestionType.rating: 3,
SnPollQuestionType.freeText: 4,
};
_SnPollOption _$SnPollOptionFromJson(Map<String, dynamic> json) =>
_SnPollOption(
id: json['id'] as String,
label: json['label'] as String,
description: json['description'] as String?,
order: (json['order'] as num).toInt(),
);
Map<String, dynamic> _$SnPollOptionToJson(_SnPollOption instance) =>
<String, dynamic>{
'id': instance.id,
'label': instance.label,
'description': instance.description,
'order': instance.order,
};
_SnPollAnswer _$SnPollAnswerFromJson(Map<String, dynamic> json) =>
_SnPollAnswer(
id: json['id'] as String,
answer: json['answer'] as Map<String, dynamic>,
accountId: json['account_id'] as String,
pollId: json['poll_id'] as String,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt:
json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
);
Map<String, dynamic> _$SnPollAnswerToJson(_SnPollAnswer instance) =>
<String, dynamic>{
'id': instance.id,
'answer': instance.answer,
'account_id': instance.accountId,
'poll_id': instance.pollId,
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
};

View File

@@ -34,9 +34,10 @@ sealed class SnPost with _$SnPost {
@Default([]) List<SnCloudFile> attachments, @Default([]) List<SnCloudFile> attachments,
required SnPublisher publisher, required SnPublisher publisher,
@Default({}) Map<String, int> reactionsCount, @Default({}) Map<String, int> reactionsCount,
@Default({}) Map<String, bool> reactionsMade,
@Default([]) List<dynamic> reactions, @Default([]) List<dynamic> reactions,
@Default([]) List<PostTag> tags, @Default([]) List<SnPostTag> tags,
@Default([]) List<PostCategory> categories, @Default([]) List<SnPostCategory> categories,
@Default([]) List<dynamic> collections, @Default([]) List<dynamic> collections,
@Default(null) DateTime? createdAt, @Default(null) DateTime? createdAt,
@Default(null) DateTime? updatedAt, @Default(null) DateTime? updatedAt,
@@ -77,6 +78,13 @@ sealed class SnSubscriptionStatus with _$SnSubscriptionStatus {
sealed class ReactInfo with _$ReactInfo { sealed class ReactInfo with _$ReactInfo {
const factory ReactInfo({required String icon, required int attitude}) = const factory ReactInfo({required String icon, required int attitude}) =
_ReactInfo; _ReactInfo;
static String getTranslationKey(String templateKey) {
final parts = templateKey.split('_');
final camelCase =
parts.map((p) => p[0].toUpperCase() + p.substring(1)).join();
return 'reaction$camelCase';
}
} }
const Map<String, ReactInfo> kReactionTemplates = { const Map<String, ReactInfo> kReactionTemplates = {

View File

@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$SnPost { mixin _$SnPost {
String get id; String? get title; String? get description; String? get language; DateTime? get editedAt; DateTime? get publishedAt; int get visibility; String? get content; int get type; Map<String, dynamic>? get meta; int get viewsUnique; int get viewsTotal; int get upvotes; int get downvotes; int get repliesCount; String? get threadedPostId; SnPost? get threadedPost; String? get repliedPostId; SnPost? get repliedPost; String? get forwardedPostId; SnPost? get forwardedPost; List<SnCloudFile> get attachments; SnPublisher get publisher; Map<String, int> get reactionsCount; List<dynamic> get reactions; List<PostTag> get tags; List<PostCategory> get categories; List<dynamic> get collections; DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; bool get isTruncated; String get id; String? get title; String? get description; String? get language; DateTime? get editedAt; DateTime? get publishedAt; int get visibility; String? get content; int get type; Map<String, dynamic>? get meta; int get viewsUnique; int get viewsTotal; int get upvotes; int get downvotes; int get repliesCount; String? get threadedPostId; SnPost? get threadedPost; String? get repliedPostId; SnPost? get repliedPost; String? get forwardedPostId; SnPost? get forwardedPost; List<SnCloudFile> get attachments; SnPublisher get publisher; Map<String, int> get reactionsCount; Map<String, bool> get reactionsMade; List<dynamic> get reactions; List<SnPostTag> get tags; List<SnPostCategory> get categories; List<dynamic> get collections; DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; bool get isTruncated;
/// Create a copy of SnPost /// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -28,16 +28,16 @@ $SnPostCopyWith<SnPost> get copyWith => _$SnPostCopyWithImpl<SnPost>(this as SnP
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.meta, meta)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&const DeepCollectionEquality().equals(other.attachments, attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other.reactionsCount, reactionsCount)&&const DeepCollectionEquality().equals(other.reactions, reactions)&&const DeepCollectionEquality().equals(other.tags, tags)&&const DeepCollectionEquality().equals(other.categories, categories)&&const DeepCollectionEquality().equals(other.collections, collections)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated)); return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.meta, meta)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&const DeepCollectionEquality().equals(other.attachments, attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other.reactionsCount, reactionsCount)&&const DeepCollectionEquality().equals(other.reactionsMade, reactionsMade)&&const DeepCollectionEquality().equals(other.reactions, reactions)&&const DeepCollectionEquality().equals(other.tags, tags)&&const DeepCollectionEquality().equals(other.categories, categories)&&const DeepCollectionEquality().equals(other.collections, collections)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,type,const DeepCollectionEquality().hash(meta),viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,const DeepCollectionEquality().hash(attachments),publisher,const DeepCollectionEquality().hash(reactionsCount),const DeepCollectionEquality().hash(reactions),const DeepCollectionEquality().hash(tags),const DeepCollectionEquality().hash(categories),const DeepCollectionEquality().hash(collections),createdAt,updatedAt,deletedAt,isTruncated]); int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,type,const DeepCollectionEquality().hash(meta),viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,const DeepCollectionEquality().hash(attachments),publisher,const DeepCollectionEquality().hash(reactionsCount),const DeepCollectionEquality().hash(reactionsMade),const DeepCollectionEquality().hash(reactions),const DeepCollectionEquality().hash(tags),const DeepCollectionEquality().hash(categories),const DeepCollectionEquality().hash(collections),createdAt,updatedAt,deletedAt,isTruncated]);
@override @override
String toString() { String toString() {
return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, type: $type, meta: $meta, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, attachments: $attachments, publisher: $publisher, reactionsCount: $reactionsCount, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, isTruncated: $isTruncated)'; return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, type: $type, meta: $meta, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, attachments: $attachments, publisher: $publisher, reactionsCount: $reactionsCount, reactionsMade: $reactionsMade, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, isTruncated: $isTruncated)';
} }
@@ -48,7 +48,7 @@ abstract mixin class $SnPostCopyWith<$Res> {
factory $SnPostCopyWith(SnPost value, $Res Function(SnPost) _then) = _$SnPostCopyWithImpl; factory $SnPostCopyWith(SnPost value, $Res Function(SnPost) _then) = _$SnPostCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, List<dynamic> reactions, List<PostTag> tags, List<PostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated
}); });
@@ -65,7 +65,7 @@ class _$SnPostCopyWithImpl<$Res>
/// Create a copy of SnPost /// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? isTruncated = null,}) { @pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactionsMade = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? isTruncated = null,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
@@ -91,10 +91,11 @@ as String?,forwardedPost: freezed == forwardedPost ? _self.forwardedPost : forwa
as SnPost?,attachments: null == attachments ? _self.attachments : attachments // ignore: cast_nullable_to_non_nullable as SnPost?,attachments: null == attachments ? _self.attachments : attachments // ignore: cast_nullable_to_non_nullable
as List<SnCloudFile>,publisher: null == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable as List<SnCloudFile>,publisher: null == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable
as SnPublisher,reactionsCount: null == reactionsCount ? _self.reactionsCount : reactionsCount // ignore: cast_nullable_to_non_nullable as SnPublisher,reactionsCount: null == reactionsCount ? _self.reactionsCount : reactionsCount // ignore: cast_nullable_to_non_nullable
as Map<String, int>,reactions: null == reactions ? _self.reactions : reactions // 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 as List<dynamic>,tags: null == tags ? _self.tags : tags // ignore: cast_nullable_to_non_nullable
as List<PostTag>,categories: null == categories ? _self.categories : categories // ignore: cast_nullable_to_non_nullable as List<SnPostTag>,categories: null == categories ? _self.categories : categories // ignore: cast_nullable_to_non_nullable
as List<PostCategory>,collections: null == collections ? _self.collections : collections // ignore: cast_nullable_to_non_nullable as List<SnPostCategory>,collections: null == collections ? _self.collections : collections // ignore: cast_nullable_to_non_nullable
as List<dynamic>,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as List<dynamic>,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime?,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?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
@@ -226,10 +227,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, List<dynamic> reactions, List<PostTag> tags, List<PostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _SnPost() when $default != null: case _SnPost() when $default != null:
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);case _: return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);case _:
return orElse(); return orElse();
} }
@@ -247,10 +248,10 @@ return $default(_that.id,_that.title,_that.description,_that.language,_that.edit
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, List<dynamic> reactions, List<PostTag> tags, List<PostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnPost(): case _SnPost():
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);} return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
/// ///
@@ -264,10 +265,10 @@ return $default(_that.id,_that.title,_that.description,_that.language,_that.edit
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, List<dynamic> reactions, List<PostTag> tags, List<PostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnPost() when $default != null: case _SnPost() when $default != null:
return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);case _: return $default(_that.id,_that.title,_that.description,_that.language,_that.editedAt,_that.publishedAt,_that.visibility,_that.content,_that.type,_that.meta,_that.viewsUnique,_that.viewsTotal,_that.upvotes,_that.downvotes,_that.repliesCount,_that.threadedPostId,_that.threadedPost,_that.repliedPostId,_that.repliedPost,_that.forwardedPostId,_that.forwardedPost,_that.attachments,_that.publisher,_that.reactionsCount,_that.reactionsMade,_that.reactions,_that.tags,_that.categories,_that.collections,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.isTruncated);case _:
return null; return null;
} }
@@ -279,7 +280,7 @@ return $default(_that.id,_that.title,_that.description,_that.language,_that.edit
@JsonSerializable() @JsonSerializable()
class _SnPost implements SnPost { class _SnPost implements SnPost {
const _SnPost({required this.id, this.title, this.description, this.language, this.editedAt, this.publishedAt = null, this.visibility = 0, this.content, this.type = 0, final Map<String, dynamic>? meta, this.viewsUnique = 0, this.viewsTotal = 0, this.upvotes = 0, this.downvotes = 0, this.repliesCount = 0, this.threadedPostId, this.threadedPost, this.repliedPostId, this.repliedPost, this.forwardedPostId, this.forwardedPost, final List<SnCloudFile> attachments = const [], required this.publisher, final Map<String, int> reactionsCount = const {}, final List<dynamic> reactions = const [], final List<PostTag> tags = const [], final List<PostCategory> categories = const [], final List<dynamic> collections = const [], this.createdAt = null, this.updatedAt = null, this.deletedAt, this.isTruncated = false}): _meta = meta,_attachments = attachments,_reactionsCount = reactionsCount,_reactions = reactions,_tags = tags,_categories = categories,_collections = collections; const _SnPost({required this.id, this.title, this.description, this.language, this.editedAt, this.publishedAt = null, this.visibility = 0, this.content, this.type = 0, final Map<String, dynamic>? meta, this.viewsUnique = 0, this.viewsTotal = 0, this.upvotes = 0, this.downvotes = 0, this.repliesCount = 0, this.threadedPostId, this.threadedPost, this.repliedPostId, this.repliedPost, this.forwardedPostId, this.forwardedPost, final List<SnCloudFile> attachments = const [], required this.publisher, final Map<String, int> reactionsCount = const {}, final Map<String, bool> reactionsMade = const {}, final List<dynamic> reactions = const [], final List<SnPostTag> tags = const [], final List<SnPostCategory> categories = const [], final List<dynamic> collections = const [], this.createdAt = null, this.updatedAt = null, this.deletedAt, this.isTruncated = false}): _meta = meta,_attachments = attachments,_reactionsCount = reactionsCount,_reactionsMade = reactionsMade,_reactions = reactions,_tags = tags,_categories = categories,_collections = collections;
factory _SnPost.fromJson(Map<String, dynamic> json) => _$SnPostFromJson(json); factory _SnPost.fromJson(Map<String, dynamic> json) => _$SnPostFromJson(json);
@override final String id; @override final String id;
@@ -326,6 +327,13 @@ class _SnPost implements SnPost {
return EqualUnmodifiableMapView(_reactionsCount); return EqualUnmodifiableMapView(_reactionsCount);
} }
final Map<String, bool> _reactionsMade;
@override@JsonKey() Map<String, bool> get reactionsMade {
if (_reactionsMade is EqualUnmodifiableMapView) return _reactionsMade;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_reactionsMade);
}
final List<dynamic> _reactions; final List<dynamic> _reactions;
@override@JsonKey() List<dynamic> get reactions { @override@JsonKey() List<dynamic> get reactions {
if (_reactions is EqualUnmodifiableListView) return _reactions; if (_reactions is EqualUnmodifiableListView) return _reactions;
@@ -333,15 +341,15 @@ class _SnPost implements SnPost {
return EqualUnmodifiableListView(_reactions); return EqualUnmodifiableListView(_reactions);
} }
final List<PostTag> _tags; final List<SnPostTag> _tags;
@override@JsonKey() List<PostTag> get tags { @override@JsonKey() List<SnPostTag> get tags {
if (_tags is EqualUnmodifiableListView) return _tags; if (_tags is EqualUnmodifiableListView) return _tags;
// ignore: implicit_dynamic_type // ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_tags); return EqualUnmodifiableListView(_tags);
} }
final List<PostCategory> _categories; final List<SnPostCategory> _categories;
@override@JsonKey() List<PostCategory> get categories { @override@JsonKey() List<SnPostCategory> get categories {
if (_categories is EqualUnmodifiableListView) return _categories; if (_categories is EqualUnmodifiableListView) return _categories;
// ignore: implicit_dynamic_type // ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_categories); return EqualUnmodifiableListView(_categories);
@@ -372,16 +380,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._meta, _meta)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&const DeepCollectionEquality().equals(other._attachments, _attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other._reactionsCount, _reactionsCount)&&const DeepCollectionEquality().equals(other._reactions, _reactions)&&const DeepCollectionEquality().equals(other._tags, _tags)&&const DeepCollectionEquality().equals(other._categories, _categories)&&const DeepCollectionEquality().equals(other._collections, _collections)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._meta, _meta)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&(identical(other.repliesCount, repliesCount) || other.repliesCount == repliesCount)&&(identical(other.threadedPostId, threadedPostId) || other.threadedPostId == threadedPostId)&&(identical(other.threadedPost, threadedPost) || other.threadedPost == threadedPost)&&(identical(other.repliedPostId, repliedPostId) || other.repliedPostId == repliedPostId)&&(identical(other.repliedPost, repliedPost) || other.repliedPost == repliedPost)&&(identical(other.forwardedPostId, forwardedPostId) || other.forwardedPostId == forwardedPostId)&&(identical(other.forwardedPost, forwardedPost) || other.forwardedPost == forwardedPost)&&const DeepCollectionEquality().equals(other._attachments, _attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other._reactionsCount, _reactionsCount)&&const DeepCollectionEquality().equals(other._reactionsMade, _reactionsMade)&&const DeepCollectionEquality().equals(other._reactions, _reactions)&&const DeepCollectionEquality().equals(other._tags, _tags)&&const DeepCollectionEquality().equals(other._categories, _categories)&&const DeepCollectionEquality().equals(other._collections, _collections)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.isTruncated, isTruncated) || other.isTruncated == isTruncated));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,type,const DeepCollectionEquality().hash(_meta),viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,const DeepCollectionEquality().hash(_attachments),publisher,const DeepCollectionEquality().hash(_reactionsCount),const DeepCollectionEquality().hash(_reactions),const DeepCollectionEquality().hash(_tags),const DeepCollectionEquality().hash(_categories),const DeepCollectionEquality().hash(_collections),createdAt,updatedAt,deletedAt,isTruncated]); int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,type,const DeepCollectionEquality().hash(_meta),viewsUnique,viewsTotal,upvotes,downvotes,repliesCount,threadedPostId,threadedPost,repliedPostId,repliedPost,forwardedPostId,forwardedPost,const DeepCollectionEquality().hash(_attachments),publisher,const DeepCollectionEquality().hash(_reactionsCount),const DeepCollectionEquality().hash(_reactionsMade),const DeepCollectionEquality().hash(_reactions),const DeepCollectionEquality().hash(_tags),const DeepCollectionEquality().hash(_categories),const DeepCollectionEquality().hash(_collections),createdAt,updatedAt,deletedAt,isTruncated]);
@override @override
String toString() { String toString() {
return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, type: $type, meta: $meta, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, attachments: $attachments, publisher: $publisher, reactionsCount: $reactionsCount, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, isTruncated: $isTruncated)'; return 'SnPost(id: $id, title: $title, description: $description, language: $language, editedAt: $editedAt, publishedAt: $publishedAt, visibility: $visibility, content: $content, type: $type, meta: $meta, viewsUnique: $viewsUnique, viewsTotal: $viewsTotal, upvotes: $upvotes, downvotes: $downvotes, repliesCount: $repliesCount, threadedPostId: $threadedPostId, threadedPost: $threadedPost, repliedPostId: $repliedPostId, repliedPost: $repliedPost, forwardedPostId: $forwardedPostId, forwardedPost: $forwardedPost, attachments: $attachments, publisher: $publisher, reactionsCount: $reactionsCount, reactionsMade: $reactionsMade, reactions: $reactions, tags: $tags, categories: $categories, collections: $collections, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, isTruncated: $isTruncated)';
} }
@@ -392,7 +400,7 @@ abstract mixin class _$SnPostCopyWith<$Res> implements $SnPostCopyWith<$Res> {
factory _$SnPostCopyWith(_SnPost value, $Res Function(_SnPost) _then) = __$SnPostCopyWithImpl; factory _$SnPostCopyWith(_SnPost value, $Res Function(_SnPost) _then) = __$SnPostCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, List<dynamic> reactions, List<PostTag> tags, List<PostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated String id, String? title, String? description, String? language, DateTime? editedAt, DateTime? publishedAt, int visibility, String? content, int type, Map<String, dynamic>? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, int repliesCount, String? threadedPostId, SnPost? threadedPost, String? repliedPostId, SnPost? repliedPost, String? forwardedPostId, SnPost? forwardedPost, List<SnCloudFile> attachments, SnPublisher publisher, Map<String, int> reactionsCount, Map<String, bool> reactionsMade, List<dynamic> reactions, List<SnPostTag> tags, List<SnPostCategory> categories, List<dynamic> collections, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, bool isTruncated
}); });
@@ -409,7 +417,7 @@ class __$SnPostCopyWithImpl<$Res>
/// Create a copy of SnPost /// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? isTruncated = null,}) { @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = freezed,Object? visibility = null,Object? content = freezed,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? repliesCount = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? attachments = null,Object? publisher = null,Object? reactionsCount = null,Object? reactionsMade = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? isTruncated = null,}) {
return _then(_SnPost( return _then(_SnPost(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable as String,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
@@ -435,10 +443,11 @@ as String?,forwardedPost: freezed == forwardedPost ? _self.forwardedPost : forwa
as SnPost?,attachments: null == attachments ? _self._attachments : attachments // ignore: cast_nullable_to_non_nullable as SnPost?,attachments: null == attachments ? _self._attachments : attachments // ignore: cast_nullable_to_non_nullable
as List<SnCloudFile>,publisher: null == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable as List<SnCloudFile>,publisher: null == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable
as SnPublisher,reactionsCount: null == reactionsCount ? _self._reactionsCount : reactionsCount // ignore: cast_nullable_to_non_nullable as SnPublisher,reactionsCount: null == reactionsCount ? _self._reactionsCount : reactionsCount // ignore: cast_nullable_to_non_nullable
as Map<String, int>,reactions: null == reactions ? _self._reactions : reactions // 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 as List<dynamic>,tags: null == tags ? _self._tags : tags // ignore: cast_nullable_to_non_nullable
as List<PostTag>,categories: null == categories ? _self._categories : categories // ignore: cast_nullable_to_non_nullable as List<SnPostTag>,categories: null == categories ? _self._categories : categories // ignore: cast_nullable_to_non_nullable
as List<PostCategory>,collections: null == collections ? _self._collections : collections // ignore: cast_nullable_to_non_nullable as List<SnPostCategory>,collections: null == collections ? _self._collections : collections // ignore: cast_nullable_to_non_nullable
as List<dynamic>,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as List<dynamic>,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime?,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?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable

View File

@@ -54,15 +54,20 @@ _SnPost _$SnPostFromJson(Map<String, dynamic> json) => _SnPost(
(k, e) => MapEntry(k, (e as num).toInt()), (k, e) => MapEntry(k, (e as num).toInt()),
) ?? ) ??
const {}, const {},
reactionsMade:
(json['reactions_made'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as bool),
) ??
const {},
reactions: json['reactions'] as List<dynamic>? ?? const [], reactions: json['reactions'] as List<dynamic>? ?? const [],
tags: tags:
(json['tags'] as List<dynamic>?) (json['tags'] as List<dynamic>?)
?.map((e) => PostTag.fromJson(e as Map<String, dynamic>)) ?.map((e) => SnPostTag.fromJson(e as Map<String, dynamic>))
.toList() ?? .toList() ??
const [], const [],
categories: categories:
(json['categories'] as List<dynamic>?) (json['categories'] as List<dynamic>?)
?.map((e) => PostCategory.fromJson(e as Map<String, dynamic>)) ?.map((e) => SnPostCategory.fromJson(e as Map<String, dynamic>))
.toList() ?? .toList() ??
const [], const [],
collections: json['collections'] as List<dynamic>? ?? const [], collections: json['collections'] as List<dynamic>? ?? const [],
@@ -106,6 +111,7 @@ Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
'attachments': instance.attachments.map((e) => e.toJson()).toList(), 'attachments': instance.attachments.map((e) => e.toJson()).toList(),
'publisher': instance.publisher.toJson(), 'publisher': instance.publisher.toJson(),
'reactions_count': instance.reactionsCount, 'reactions_count': instance.reactionsCount,
'reactions_made': instance.reactionsMade,
'reactions': instance.reactions, 'reactions': instance.reactions,
'tags': instance.tags.map((e) => e.toJson()).toList(), 'tags': instance.tags.map((e) => e.toJson()).toList(),
'categories': instance.categories.map((e) => e.toJson()).toList(), 'categories': instance.categories.map((e) => e.toJson()).toList(),

View File

@@ -1,19 +1,30 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:island/models/post.dart'; import 'package:island/models/post.dart';
import 'package:island/services/text.dart';
part 'post_category.freezed.dart'; part 'post_category.freezed.dart';
part 'post_category.g.dart'; part 'post_category.g.dart';
@freezed @freezed
sealed class PostCategory with _$PostCategory { sealed class SnPostCategory with _$SnPostCategory {
const factory PostCategory({ const SnPostCategory._();
const factory SnPostCategory({
required String id, required String id,
required String slug, required String slug,
String? name, String? name,
@Default([]) List<SnPost> posts, @Default([]) List<SnPost> posts,
}) = _PostCategory; }) = _SnPostCategory;
factory PostCategory.fromJson(Map<String, dynamic> json) => factory SnPostCategory.fromJson(Map<String, dynamic> json) =>
_$PostCategoryFromJson(json); _$SnPostCategoryFromJson(json);
String get categoryDisplayTitle {
final capitalizedSlug = slug.capitalizeEachWord();
if ('postCategory$capitalizedSlug'.trExists()) {
return 'postCategory$capitalizedSlug'.tr();
}
return name ?? slug;
}
} }

View File

@@ -13,22 +13,22 @@ part of 'post_category.dart';
T _$identity<T>(T value) => value; T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$PostCategory { mixin _$SnPostCategory {
String get id; String get slug; String? get name; List<SnPost> get posts; String get id; String get slug; String? get name; List<SnPost> get posts;
/// Create a copy of PostCategory /// Create a copy of SnPostCategory
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
$PostCategoryCopyWith<PostCategory> get copyWith => _$PostCategoryCopyWithImpl<PostCategory>(this as PostCategory, _$identity); $SnPostCategoryCopyWith<SnPostCategory> get copyWith => _$SnPostCategoryCopyWithImpl<SnPostCategory>(this as SnPostCategory, _$identity);
/// Serializes this PostCategory to a JSON map. /// Serializes this SnPostCategory to a JSON map.
Map<String, dynamic> toJson(); Map<String, dynamic> toJson();
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PostCategory&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other.posts, posts)); return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPostCategory&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other.posts, posts));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -37,15 +37,15 @@ int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEqu
@override @override
String toString() { String toString() {
return 'PostCategory(id: $id, slug: $slug, name: $name, posts: $posts)'; return 'SnPostCategory(id: $id, slug: $slug, name: $name, posts: $posts)';
} }
} }
/// @nodoc /// @nodoc
abstract mixin class $PostCategoryCopyWith<$Res> { abstract mixin class $SnPostCategoryCopyWith<$Res> {
factory $PostCategoryCopyWith(PostCategory value, $Res Function(PostCategory) _then) = _$PostCategoryCopyWithImpl; factory $SnPostCategoryCopyWith(SnPostCategory value, $Res Function(SnPostCategory) _then) = _$SnPostCategoryCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String id, String slug, String? name, List<SnPost> posts String id, String slug, String? name, List<SnPost> posts
@@ -56,14 +56,14 @@ $Res call({
} }
/// @nodoc /// @nodoc
class _$PostCategoryCopyWithImpl<$Res> class _$SnPostCategoryCopyWithImpl<$Res>
implements $PostCategoryCopyWith<$Res> { implements $SnPostCategoryCopyWith<$Res> {
_$PostCategoryCopyWithImpl(this._self, this._then); _$SnPostCategoryCopyWithImpl(this._self, this._then);
final PostCategory _self; final SnPostCategory _self;
final $Res Function(PostCategory) _then; final $Res Function(SnPostCategory) _then;
/// Create a copy of PostCategory /// Create a copy of SnPostCategory
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,}) { @pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
@@ -78,8 +78,8 @@ as List<SnPost>,
} }
/// Adds pattern-matching-related methods to [PostCategory]. /// Adds pattern-matching-related methods to [SnPostCategory].
extension PostCategoryPatterns on PostCategory { extension SnPostCategoryPatterns on SnPostCategory {
/// A variant of `map` that fallback to returning `orElse`. /// A variant of `map` that fallback to returning `orElse`.
/// ///
/// It is equivalent to doing: /// It is equivalent to doing:
@@ -92,10 +92,10 @@ extension PostCategoryPatterns on PostCategory {
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _PostCategory value)? $default,{required TResult orElse(),}){ @optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnPostCategory value)? $default,{required TResult orElse(),}){
final _that = this; final _that = this;
switch (_that) { switch (_that) {
case _PostCategory() when $default != null: case _SnPostCategory() when $default != null:
return $default(_that);case _: return $default(_that);case _:
return orElse(); return orElse();
@@ -114,10 +114,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _PostCategory value) $default,){ @optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnPostCategory value) $default,){
final _that = this; final _that = this;
switch (_that) { switch (_that) {
case _PostCategory(): case _SnPostCategory():
return $default(_that);} return $default(_that);}
} }
/// A variant of `map` that fallback to returning `null`. /// A variant of `map` that fallback to returning `null`.
@@ -132,10 +132,10 @@ return $default(_that);}
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _PostCategory value)? $default,){ @optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnPostCategory value)? $default,){
final _that = this; final _that = this;
switch (_that) { switch (_that) {
case _PostCategory() when $default != null: case _SnPostCategory() when $default != null:
return $default(_that);case _: return $default(_that);case _:
return null; return null;
@@ -155,7 +155,7 @@ return $default(_that);case _:
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _PostCategory() when $default != null: case _SnPostCategory() when $default != null:
return $default(_that.id,_that.slug,_that.name,_that.posts);case _: return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
return orElse(); return orElse();
@@ -176,7 +176,7 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _PostCategory(): case _SnPostCategory():
return $default(_that.id,_that.slug,_that.name,_that.posts);} return $default(_that.id,_that.slug,_that.name,_that.posts);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
@@ -193,7 +193,7 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);}
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String slug, String? name, List<SnPost> posts)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String slug, String? name, List<SnPost> posts)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _PostCategory() when $default != null: case _SnPostCategory() when $default != null:
return $default(_that.id,_that.slug,_that.name,_that.posts);case _: return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
return null; return null;
@@ -205,9 +205,9 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
/// @nodoc /// @nodoc
@JsonSerializable() @JsonSerializable()
class _PostCategory implements PostCategory { class _SnPostCategory extends SnPostCategory {
const _PostCategory({required this.id, required this.slug, this.name, final List<SnPost> posts = const []}): _posts = posts; const _SnPostCategory({required this.id, required this.slug, this.name, final List<SnPost> posts = const []}): _posts = posts,super._();
factory _PostCategory.fromJson(Map<String, dynamic> json) => _$PostCategoryFromJson(json); factory _SnPostCategory.fromJson(Map<String, dynamic> json) => _$SnPostCategoryFromJson(json);
@override final String id; @override final String id;
@override final String slug; @override final String slug;
@@ -220,20 +220,20 @@ class _PostCategory implements PostCategory {
} }
/// Create a copy of PostCategory /// Create a copy of SnPostCategory
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false) @override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$PostCategoryCopyWith<_PostCategory> get copyWith => __$PostCategoryCopyWithImpl<_PostCategory>(this, _$identity); _$SnPostCategoryCopyWith<_SnPostCategory> get copyWith => __$SnPostCategoryCopyWithImpl<_SnPostCategory>(this, _$identity);
@override @override
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return _$PostCategoryToJson(this, ); return _$SnPostCategoryToJson(this, );
} }
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PostCategory&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other._posts, _posts)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPostCategory&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other._posts, _posts));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -242,15 +242,15 @@ int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEqu
@override @override
String toString() { String toString() {
return 'PostCategory(id: $id, slug: $slug, name: $name, posts: $posts)'; return 'SnPostCategory(id: $id, slug: $slug, name: $name, posts: $posts)';
} }
} }
/// @nodoc /// @nodoc
abstract mixin class _$PostCategoryCopyWith<$Res> implements $PostCategoryCopyWith<$Res> { abstract mixin class _$SnPostCategoryCopyWith<$Res> implements $SnPostCategoryCopyWith<$Res> {
factory _$PostCategoryCopyWith(_PostCategory value, $Res Function(_PostCategory) _then) = __$PostCategoryCopyWithImpl; factory _$SnPostCategoryCopyWith(_SnPostCategory value, $Res Function(_SnPostCategory) _then) = __$SnPostCategoryCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String id, String slug, String? name, List<SnPost> posts String id, String slug, String? name, List<SnPost> posts
@@ -261,17 +261,17 @@ $Res call({
} }
/// @nodoc /// @nodoc
class __$PostCategoryCopyWithImpl<$Res> class __$SnPostCategoryCopyWithImpl<$Res>
implements _$PostCategoryCopyWith<$Res> { implements _$SnPostCategoryCopyWith<$Res> {
__$PostCategoryCopyWithImpl(this._self, this._then); __$SnPostCategoryCopyWithImpl(this._self, this._then);
final _PostCategory _self; final _SnPostCategory _self;
final $Res Function(_PostCategory) _then; final $Res Function(_SnPostCategory) _then;
/// Create a copy of PostCategory /// Create a copy of SnPostCategory
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,}) { @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,}) {
return _then(_PostCategory( return _then(_SnPostCategory(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable

View File

@@ -6,8 +6,8 @@ part of 'post_category.dart';
// JsonSerializableGenerator // JsonSerializableGenerator
// ************************************************************************** // **************************************************************************
_PostCategory _$PostCategoryFromJson(Map<String, dynamic> json) => _SnPostCategory _$SnPostCategoryFromJson(Map<String, dynamic> json) =>
_PostCategory( _SnPostCategory(
id: json['id'] as String, id: json['id'] as String,
slug: json['slug'] as String, slug: json['slug'] as String,
name: json['name'] as String?, name: json['name'] as String?,
@@ -18,7 +18,7 @@ _PostCategory _$PostCategoryFromJson(Map<String, dynamic> json) =>
const [], const [],
); );
Map<String, dynamic> _$PostCategoryToJson(_PostCategory instance) => Map<String, dynamic> _$SnPostCategoryToJson(_SnPostCategory instance) =>
<String, dynamic>{ <String, dynamic>{
'id': instance.id, 'id': instance.id,
'slug': instance.slug, 'slug': instance.slug,

View File

@@ -1,4 +1,3 @@
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:island/models/post.dart'; import 'package:island/models/post.dart';
@@ -6,14 +5,14 @@ part 'post_tag.freezed.dart';
part 'post_tag.g.dart'; part 'post_tag.g.dart';
@freezed @freezed
sealed class PostTag with _$PostTag { sealed class SnPostTag with _$SnPostTag {
const factory PostTag({ const factory SnPostTag({
required String id, required String id,
required String slug, required String slug,
String? name, String? name,
@Default([]) List<SnPost> posts, @Default([]) List<SnPost> posts,
}) = _PostTag; }) = _SnPostTag;
factory PostTag.fromJson(Map<String, dynamic> json) => factory SnPostTag.fromJson(Map<String, dynamic> json) =>
_$PostTagFromJson(json); _$SnPostTagFromJson(json);
} }

View File

@@ -13,22 +13,22 @@ part of 'post_tag.dart';
T _$identity<T>(T value) => value; T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$PostTag { mixin _$SnPostTag {
String get id; String get slug; String? get name; List<SnPost> get posts; String get id; String get slug; String? get name; List<SnPost> get posts;
/// Create a copy of PostTag /// Create a copy of SnPostTag
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
$PostTagCopyWith<PostTag> get copyWith => _$PostTagCopyWithImpl<PostTag>(this as PostTag, _$identity); $SnPostTagCopyWith<SnPostTag> get copyWith => _$SnPostTagCopyWithImpl<SnPostTag>(this as SnPostTag, _$identity);
/// Serializes this PostTag to a JSON map. /// Serializes this SnPostTag to a JSON map.
Map<String, dynamic> toJson(); Map<String, dynamic> toJson();
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PostTag&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other.posts, posts)); return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPostTag&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other.posts, posts));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -37,15 +37,15 @@ int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEqu
@override @override
String toString() { String toString() {
return 'PostTag(id: $id, slug: $slug, name: $name, posts: $posts)'; return 'SnPostTag(id: $id, slug: $slug, name: $name, posts: $posts)';
} }
} }
/// @nodoc /// @nodoc
abstract mixin class $PostTagCopyWith<$Res> { abstract mixin class $SnPostTagCopyWith<$Res> {
factory $PostTagCopyWith(PostTag value, $Res Function(PostTag) _then) = _$PostTagCopyWithImpl; factory $SnPostTagCopyWith(SnPostTag value, $Res Function(SnPostTag) _then) = _$SnPostTagCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String id, String slug, String? name, List<SnPost> posts String id, String slug, String? name, List<SnPost> posts
@@ -56,14 +56,14 @@ $Res call({
} }
/// @nodoc /// @nodoc
class _$PostTagCopyWithImpl<$Res> class _$SnPostTagCopyWithImpl<$Res>
implements $PostTagCopyWith<$Res> { implements $SnPostTagCopyWith<$Res> {
_$PostTagCopyWithImpl(this._self, this._then); _$SnPostTagCopyWithImpl(this._self, this._then);
final PostTag _self; final SnPostTag _self;
final $Res Function(PostTag) _then; final $Res Function(SnPostTag) _then;
/// Create a copy of PostTag /// Create a copy of SnPostTag
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,}) { @pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
@@ -78,8 +78,8 @@ as List<SnPost>,
} }
/// Adds pattern-matching-related methods to [PostTag]. /// Adds pattern-matching-related methods to [SnPostTag].
extension PostTagPatterns on PostTag { extension SnPostTagPatterns on SnPostTag {
/// A variant of `map` that fallback to returning `orElse`. /// A variant of `map` that fallback to returning `orElse`.
/// ///
/// It is equivalent to doing: /// It is equivalent to doing:
@@ -92,10 +92,10 @@ extension PostTagPatterns on PostTag {
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _PostTag value)? $default,{required TResult orElse(),}){ @optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnPostTag value)? $default,{required TResult orElse(),}){
final _that = this; final _that = this;
switch (_that) { switch (_that) {
case _PostTag() when $default != null: case _SnPostTag() when $default != null:
return $default(_that);case _: return $default(_that);case _:
return orElse(); return orElse();
@@ -114,10 +114,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _PostTag value) $default,){ @optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnPostTag value) $default,){
final _that = this; final _that = this;
switch (_that) { switch (_that) {
case _PostTag(): case _SnPostTag():
return $default(_that);} return $default(_that);}
} }
/// A variant of `map` that fallback to returning `null`. /// A variant of `map` that fallback to returning `null`.
@@ -132,10 +132,10 @@ return $default(_that);}
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _PostTag value)? $default,){ @optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnPostTag value)? $default,){
final _that = this; final _that = this;
switch (_that) { switch (_that) {
case _PostTag() when $default != null: case _SnPostTag() when $default != null:
return $default(_that);case _: return $default(_that);case _:
return null; return null;
@@ -155,7 +155,7 @@ return $default(_that);case _:
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _PostTag() when $default != null: case _SnPostTag() when $default != null:
return $default(_that.id,_that.slug,_that.name,_that.posts);case _: return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
return orElse(); return orElse();
@@ -176,7 +176,7 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _PostTag(): case _SnPostTag():
return $default(_that.id,_that.slug,_that.name,_that.posts);} return $default(_that.id,_that.slug,_that.name,_that.posts);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
@@ -193,7 +193,7 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);}
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String slug, String? name, List<SnPost> posts)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String slug, String? name, List<SnPost> posts)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _PostTag() when $default != null: case _SnPostTag() when $default != null:
return $default(_that.id,_that.slug,_that.name,_that.posts);case _: return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
return null; return null;
@@ -205,9 +205,9 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
/// @nodoc /// @nodoc
@JsonSerializable() @JsonSerializable()
class _PostTag implements PostTag { class _SnPostTag implements SnPostTag {
const _PostTag({required this.id, required this.slug, this.name, final List<SnPost> posts = const []}): _posts = posts; const _SnPostTag({required this.id, required this.slug, this.name, final List<SnPost> posts = const []}): _posts = posts;
factory _PostTag.fromJson(Map<String, dynamic> json) => _$PostTagFromJson(json); factory _SnPostTag.fromJson(Map<String, dynamic> json) => _$SnPostTagFromJson(json);
@override final String id; @override final String id;
@override final String slug; @override final String slug;
@@ -220,20 +220,20 @@ class _PostTag implements PostTag {
} }
/// Create a copy of PostTag /// Create a copy of SnPostTag
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false) @override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$PostTagCopyWith<_PostTag> get copyWith => __$PostTagCopyWithImpl<_PostTag>(this, _$identity); _$SnPostTagCopyWith<_SnPostTag> get copyWith => __$SnPostTagCopyWithImpl<_SnPostTag>(this, _$identity);
@override @override
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return _$PostTagToJson(this, ); return _$SnPostTagToJson(this, );
} }
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PostTag&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other._posts, _posts)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPostTag&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other._posts, _posts));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -242,15 +242,15 @@ int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEqu
@override @override
String toString() { String toString() {
return 'PostTag(id: $id, slug: $slug, name: $name, posts: $posts)'; return 'SnPostTag(id: $id, slug: $slug, name: $name, posts: $posts)';
} }
} }
/// @nodoc /// @nodoc
abstract mixin class _$PostTagCopyWith<$Res> implements $PostTagCopyWith<$Res> { abstract mixin class _$SnPostTagCopyWith<$Res> implements $SnPostTagCopyWith<$Res> {
factory _$PostTagCopyWith(_PostTag value, $Res Function(_PostTag) _then) = __$PostTagCopyWithImpl; factory _$SnPostTagCopyWith(_SnPostTag value, $Res Function(_SnPostTag) _then) = __$SnPostTagCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String id, String slug, String? name, List<SnPost> posts String id, String slug, String? name, List<SnPost> posts
@@ -261,17 +261,17 @@ $Res call({
} }
/// @nodoc /// @nodoc
class __$PostTagCopyWithImpl<$Res> class __$SnPostTagCopyWithImpl<$Res>
implements _$PostTagCopyWith<$Res> { implements _$SnPostTagCopyWith<$Res> {
__$PostTagCopyWithImpl(this._self, this._then); __$SnPostTagCopyWithImpl(this._self, this._then);
final _PostTag _self; final _SnPostTag _self;
final $Res Function(_PostTag) _then; final $Res Function(_SnPostTag) _then;
/// Create a copy of PostTag /// Create a copy of SnPostTag
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,}) { @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,}) {
return _then(_PostTag( return _then(_SnPostTag(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable

View File

@@ -6,7 +6,7 @@ part of 'post_tag.dart';
// JsonSerializableGenerator // JsonSerializableGenerator
// ************************************************************************** // **************************************************************************
_PostTag _$PostTagFromJson(Map<String, dynamic> json) => _PostTag( _SnPostTag _$SnPostTagFromJson(Map<String, dynamic> json) => _SnPostTag(
id: json['id'] as String, id: json['id'] as String,
slug: json['slug'] as String, slug: json['slug'] as String,
name: json['name'] as String?, name: json['name'] as String?,
@@ -17,9 +17,10 @@ _PostTag _$PostTagFromJson(Map<String, dynamic> json) => _PostTag(
const [], const [],
); );
Map<String, dynamic> _$PostTagToJson(_PostTag instance) => <String, dynamic>{ Map<String, dynamic> _$SnPostTagToJson(_SnPostTag instance) =>
<String, dynamic>{
'id': instance.id, 'id': instance.id,
'slug': instance.slug, 'slug': instance.slug,
'name': instance.name, 'name': instance.name,
'posts': instance.posts.map((e) => e.toJson()).toList(), 'posts': instance.posts.map((e) => e.toJson()).toList(),
}; };

View File

@@ -35,6 +35,7 @@ sealed class SnStickerPack with _$SnStickerPack {
required DateTime createdAt, required DateTime createdAt,
required DateTime updatedAt, required DateTime updatedAt,
required DateTime? deletedAt, required DateTime? deletedAt,
@Default([]) List<SnSticker> stickers,
}) = _SnStickerPack; }) = _SnStickerPack;
factory SnStickerPack.fromJson(Map<String, dynamic> json) => factory SnStickerPack.fromJson(Map<String, dynamic> json) =>

View File

@@ -338,7 +338,7 @@ $SnStickerPackCopyWith<$Res>? get pack {
/// @nodoc /// @nodoc
mixin _$SnStickerPack { mixin _$SnStickerPack {
String get id; String get name; String get description; String get prefix; String get publisherId; SnPublisher? get publisher; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String get id; String get name; String get description; String get prefix; String get publisherId; SnPublisher? get publisher; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; List<SnSticker> get stickers;
/// Create a copy of SnStickerPack /// Create a copy of SnStickerPack
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -351,16 +351,16 @@ $SnStickerPackCopyWith<SnStickerPack> get copyWith => _$SnStickerPackCopyWithImp
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnStickerPack&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.prefix, prefix) || other.prefix == prefix)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&(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 SnStickerPack&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.prefix, prefix) || other.prefix == prefix)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&const DeepCollectionEquality().equals(other.stickers, stickers));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,id,name,description,prefix,publisherId,publisher,createdAt,updatedAt,deletedAt); int get hashCode => Object.hash(runtimeType,id,name,description,prefix,publisherId,publisher,createdAt,updatedAt,deletedAt,const DeepCollectionEquality().hash(stickers));
@override @override
String toString() { String toString() {
return 'SnStickerPack(id: $id, name: $name, description: $description, prefix: $prefix, publisherId: $publisherId, publisher: $publisher, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; return 'SnStickerPack(id: $id, name: $name, description: $description, prefix: $prefix, publisherId: $publisherId, publisher: $publisher, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, stickers: $stickers)';
} }
@@ -371,7 +371,7 @@ abstract mixin class $SnStickerPackCopyWith<$Res> {
factory $SnStickerPackCopyWith(SnStickerPack value, $Res Function(SnStickerPack) _then) = _$SnStickerPackCopyWithImpl; factory $SnStickerPackCopyWith(SnStickerPack value, $Res Function(SnStickerPack) _then) = _$SnStickerPackCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String id, String name, String description, String prefix, String publisherId, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt String id, String name, String description, String prefix, String publisherId, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, List<SnSticker> stickers
}); });
@@ -388,7 +388,7 @@ class _$SnStickerPackCopyWithImpl<$Res>
/// Create a copy of SnStickerPack /// Create a copy of SnStickerPack
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? description = null,Object? prefix = null,Object? publisherId = null,Object? publisher = 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 = null,Object? prefix = null,Object? publisherId = null,Object? publisher = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? stickers = null,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
@@ -399,7 +399,8 @@ as String,publisher: freezed == publisher ? _self.publisher : publisher // ignor
as SnPublisher?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as SnPublisher?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?, as DateTime?,stickers: null == stickers ? _self.stickers : stickers // ignore: cast_nullable_to_non_nullable
as List<SnSticker>,
)); ));
} }
/// Create a copy of SnStickerPack /// Create a copy of SnStickerPack
@@ -493,10 +494,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String name, String description, String prefix, String publisherId, SnPublisher? publisher, 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, String prefix, String publisherId, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, List<SnSticker> stickers)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _SnStickerPack() when $default != null: case _SnStickerPack() when $default != null:
return $default(_that.id,_that.name,_that.description,_that.prefix,_that.publisherId,_that.publisher,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return $default(_that.id,_that.name,_that.description,_that.prefix,_that.publisherId,_that.publisher,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.stickers);case _:
return orElse(); return orElse();
} }
@@ -514,10 +515,10 @@ return $default(_that.id,_that.name,_that.description,_that.prefix,_that.publish
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String name, String description, String prefix, String publisherId, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String name, String description, String prefix, String publisherId, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, List<SnSticker> stickers) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnStickerPack(): case _SnStickerPack():
return $default(_that.id,_that.name,_that.description,_that.prefix,_that.publisherId,_that.publisher,_that.createdAt,_that.updatedAt,_that.deletedAt);} return $default(_that.id,_that.name,_that.description,_that.prefix,_that.publisherId,_that.publisher,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.stickers);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
/// ///
@@ -531,10 +532,10 @@ return $default(_that.id,_that.name,_that.description,_that.prefix,_that.publish
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String name, String description, String prefix, String publisherId, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String name, String description, String prefix, String publisherId, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, List<SnSticker> stickers)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnStickerPack() when $default != null: case _SnStickerPack() when $default != null:
return $default(_that.id,_that.name,_that.description,_that.prefix,_that.publisherId,_that.publisher,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return $default(_that.id,_that.name,_that.description,_that.prefix,_that.publisherId,_that.publisher,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.stickers);case _:
return null; return null;
} }
@@ -546,7 +547,7 @@ return $default(_that.id,_that.name,_that.description,_that.prefix,_that.publish
@JsonSerializable() @JsonSerializable()
class _SnStickerPack implements SnStickerPack { class _SnStickerPack implements SnStickerPack {
const _SnStickerPack({required this.id, required this.name, required this.description, required this.prefix, required this.publisherId, required this.publisher, required this.createdAt, required this.updatedAt, required this.deletedAt}); const _SnStickerPack({required this.id, required this.name, required this.description, required this.prefix, required this.publisherId, required this.publisher, required this.createdAt, required this.updatedAt, required this.deletedAt, final List<SnSticker> stickers = const []}): _stickers = stickers;
factory _SnStickerPack.fromJson(Map<String, dynamic> json) => _$SnStickerPackFromJson(json); factory _SnStickerPack.fromJson(Map<String, dynamic> json) => _$SnStickerPackFromJson(json);
@override final String id; @override final String id;
@@ -558,6 +559,13 @@ class _SnStickerPack implements SnStickerPack {
@override final DateTime createdAt; @override final DateTime createdAt;
@override final DateTime updatedAt; @override final DateTime updatedAt;
@override final DateTime? deletedAt; @override final DateTime? deletedAt;
final List<SnSticker> _stickers;
@override@JsonKey() List<SnSticker> get stickers {
if (_stickers is EqualUnmodifiableListView) return _stickers;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_stickers);
}
/// Create a copy of SnStickerPack /// Create a copy of SnStickerPack
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -572,16 +580,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnStickerPack&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.prefix, prefix) || other.prefix == prefix)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&(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 _SnStickerPack&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.prefix, prefix) || other.prefix == prefix)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&const DeepCollectionEquality().equals(other._stickers, _stickers));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,id,name,description,prefix,publisherId,publisher,createdAt,updatedAt,deletedAt); int get hashCode => Object.hash(runtimeType,id,name,description,prefix,publisherId,publisher,createdAt,updatedAt,deletedAt,const DeepCollectionEquality().hash(_stickers));
@override @override
String toString() { String toString() {
return 'SnStickerPack(id: $id, name: $name, description: $description, prefix: $prefix, publisherId: $publisherId, publisher: $publisher, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; return 'SnStickerPack(id: $id, name: $name, description: $description, prefix: $prefix, publisherId: $publisherId, publisher: $publisher, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, stickers: $stickers)';
} }
@@ -592,7 +600,7 @@ abstract mixin class _$SnStickerPackCopyWith<$Res> implements $SnStickerPackCopy
factory _$SnStickerPackCopyWith(_SnStickerPack value, $Res Function(_SnStickerPack) _then) = __$SnStickerPackCopyWithImpl; factory _$SnStickerPackCopyWith(_SnStickerPack value, $Res Function(_SnStickerPack) _then) = __$SnStickerPackCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String id, String name, String description, String prefix, String publisherId, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt String id, String name, String description, String prefix, String publisherId, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, List<SnSticker> stickers
}); });
@@ -609,7 +617,7 @@ class __$SnStickerPackCopyWithImpl<$Res>
/// Create a copy of SnStickerPack /// Create a copy of SnStickerPack
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? description = null,Object? prefix = null,Object? publisherId = null,Object? publisher = 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 = null,Object? prefix = null,Object? publisherId = null,Object? publisher = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? stickers = null,}) {
return _then(_SnStickerPack( return _then(_SnStickerPack(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
@@ -620,7 +628,8 @@ as String,publisher: freezed == publisher ? _self.publisher : publisher // ignor
as SnPublisher?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as SnPublisher?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?, as DateTime?,stickers: null == stickers ? _self._stickers : stickers // ignore: cast_nullable_to_non_nullable
as List<SnSticker>,
)); ));
} }

View File

@@ -54,6 +54,11 @@ _SnStickerPack _$SnStickerPackFromJson(Map<String, dynamic> json) =>
json['deleted_at'] == null json['deleted_at'] == null
? null ? null
: DateTime.parse(json['deleted_at'] as String), : DateTime.parse(json['deleted_at'] as String),
stickers:
(json['stickers'] as List<dynamic>?)
?.map((e) => SnSticker.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
); );
Map<String, dynamic> _$SnStickerPackToJson(_SnStickerPack instance) => Map<String, dynamic> _$SnStickerPackToJson(_SnStickerPack instance) =>
@@ -67,4 +72,5 @@ Map<String, dynamic> _$SnStickerPackToJson(_SnStickerPack instance) =>
'created_at': instance.createdAt.toIso8601String(), 'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(), 'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(), 'deleted_at': instance.deletedAt?.toIso8601String(),
'stickers': instance.stickers.map((e) => e.toJson()).toList(),
}; };

View File

@@ -38,6 +38,7 @@ sealed class SnAccountProfile with _$SnAccountProfile {
@Default('') String location, @Default('') String location,
@Default('') String timeZone, @Default('') String timeZone,
DateTime? birthday, DateTime? birthday,
@Default({}) Map<String, String> links,
DateTime? lastSeenAt, DateTime? lastSeenAt,
SnAccountBadge? activeBadge, SnAccountBadge? activeBadge,
required int experience, required int experience,

View File

@@ -350,7 +350,7 @@ $SnWalletSubscriptionRefCopyWith<$Res>? get perkSubscription {
/// @nodoc /// @nodoc
mixin _$SnAccountProfile { mixin _$SnAccountProfile {
String get id; String get firstName; String get middleName; String get lastName; String get bio; String get gender; String get pronouns; String get location; String get timeZone; DateTime? get birthday; DateTime? get lastSeenAt; SnAccountBadge? get activeBadge; int get experience; int get level; double get levelingProgress; SnCloudFile? get picture; SnCloudFile? get background; SnVerificationMark? get verification; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String get id; String get firstName; String get middleName; String get lastName; String get bio; String get gender; String get pronouns; String get location; String get timeZone; DateTime? get birthday; Map<String, String> get links; DateTime? get lastSeenAt; SnAccountBadge? get activeBadge; int get experience; int get level; double get levelingProgress; SnCloudFile? get picture; SnCloudFile? get background; SnVerificationMark? get verification; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
/// Create a copy of SnAccountProfile /// Create a copy of SnAccountProfile
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -363,16 +363,16 @@ $SnAccountProfileCopyWith<SnAccountProfile> get copyWith => _$SnAccountProfileCo
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(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 SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&const DeepCollectionEquality().equals(other.links, links)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,lastSeenAt,activeBadge,experience,level,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt]); int get hashCode => Object.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,const DeepCollectionEquality().hash(links),lastSeenAt,activeBadge,experience,level,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt]);
@override @override
String toString() { String toString() {
return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, links: $links, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
} }
@@ -383,7 +383,7 @@ abstract mixin class $SnAccountProfileCopyWith<$Res> {
factory $SnAccountProfileCopyWith(SnAccountProfile value, $Res Function(SnAccountProfile) _then) = _$SnAccountProfileCopyWithImpl; factory $SnAccountProfileCopyWith(SnAccountProfile value, $Res Function(SnAccountProfile) _then) = _$SnAccountProfileCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, Map<String, String> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
}); });
@@ -400,7 +400,7 @@ class _$SnAccountProfileCopyWithImpl<$Res>
/// Create a copy of SnAccountProfile /// Create a copy of SnAccountProfile
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { @pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? links = null,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
@@ -412,7 +412,8 @@ as String,pronouns: null == pronouns ? _self.pronouns : pronouns // ignore: cast
as String,location: null == location ? _self.location : location // ignore: cast_nullable_to_non_nullable as String,location: null == location ? _self.location : location // ignore: cast_nullable_to_non_nullable
as String,timeZone: null == timeZone ? _self.timeZone : timeZone // ignore: cast_nullable_to_non_nullable as String,timeZone: null == timeZone ? _self.timeZone : timeZone // ignore: cast_nullable_to_non_nullable
as String,birthday: freezed == birthday ? _self.birthday : birthday // ignore: cast_nullable_to_non_nullable as String,birthday: freezed == birthday ? _self.birthday : birthday // ignore: cast_nullable_to_non_nullable
as DateTime?,lastSeenAt: freezed == lastSeenAt ? _self.lastSeenAt : lastSeenAt // ignore: cast_nullable_to_non_nullable as DateTime?,links: null == links ? _self.links : links // ignore: cast_nullable_to_non_nullable
as Map<String, String>,lastSeenAt: freezed == lastSeenAt ? _self.lastSeenAt : lastSeenAt // ignore: cast_nullable_to_non_nullable
as DateTime?,activeBadge: freezed == activeBadge ? _self.activeBadge : activeBadge // ignore: cast_nullable_to_non_nullable as DateTime?,activeBadge: freezed == activeBadge ? _self.activeBadge : activeBadge // ignore: cast_nullable_to_non_nullable
as SnAccountBadge?,experience: null == experience ? _self.experience : experience // ignore: cast_nullable_to_non_nullable as SnAccountBadge?,experience: null == experience ? _self.experience : experience // ignore: cast_nullable_to_non_nullable
as int,level: null == level ? _self.level : level // ignore: cast_nullable_to_non_nullable as int,level: null == level ? _self.level : level // ignore: cast_nullable_to_non_nullable
@@ -553,10 +554,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, Map<String, String> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _SnAccountProfile() when $default != null: case _SnAccountProfile() when $default != null:
return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.links,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
return orElse(); return orElse();
} }
@@ -574,10 +575,10 @@ return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.b
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, Map<String, String> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnAccountProfile(): case _SnAccountProfile():
return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);} return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.links,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
/// ///
@@ -591,10 +592,10 @@ return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.b
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, Map<String, String> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnAccountProfile() when $default != null: case _SnAccountProfile() when $default != null:
return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.bio,_that.gender,_that.pronouns,_that.location,_that.timeZone,_that.birthday,_that.links,_that.lastSeenAt,_that.activeBadge,_that.experience,_that.level,_that.levelingProgress,_that.picture,_that.background,_that.verification,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
return null; return null;
} }
@@ -606,7 +607,7 @@ return $default(_that.id,_that.firstName,_that.middleName,_that.lastName,_that.b
@JsonSerializable() @JsonSerializable()
class _SnAccountProfile implements SnAccountProfile { class _SnAccountProfile implements SnAccountProfile {
const _SnAccountProfile({required this.id, this.firstName = '', this.middleName = '', this.lastName = '', this.bio = '', this.gender = '', this.pronouns = '', this.location = '', this.timeZone = '', this.birthday, this.lastSeenAt, this.activeBadge, required this.experience, required this.level, required this.levelingProgress, required this.picture, required this.background, required this.verification, required this.createdAt, required this.updatedAt, required this.deletedAt}); const _SnAccountProfile({required this.id, this.firstName = '', this.middleName = '', this.lastName = '', this.bio = '', this.gender = '', this.pronouns = '', this.location = '', this.timeZone = '', this.birthday, final Map<String, String> links = const {}, this.lastSeenAt, this.activeBadge, required this.experience, required this.level, required this.levelingProgress, required this.picture, required this.background, required this.verification, required this.createdAt, required this.updatedAt, required this.deletedAt}): _links = links;
factory _SnAccountProfile.fromJson(Map<String, dynamic> json) => _$SnAccountProfileFromJson(json); factory _SnAccountProfile.fromJson(Map<String, dynamic> json) => _$SnAccountProfileFromJson(json);
@override final String id; @override final String id;
@@ -619,6 +620,13 @@ class _SnAccountProfile implements SnAccountProfile {
@override@JsonKey() final String location; @override@JsonKey() final String location;
@override@JsonKey() final String timeZone; @override@JsonKey() final String timeZone;
@override final DateTime? birthday; @override final DateTime? birthday;
final Map<String, String> _links;
@override@JsonKey() Map<String, String> get links {
if (_links is EqualUnmodifiableMapView) return _links;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_links);
}
@override final DateTime? lastSeenAt; @override final DateTime? lastSeenAt;
@override final SnAccountBadge? activeBadge; @override final SnAccountBadge? activeBadge;
@override final int experience; @override final int experience;
@@ -644,16 +652,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(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 _SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&const DeepCollectionEquality().equals(other._links, _links)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,lastSeenAt,activeBadge,experience,level,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt]); int get hashCode => Object.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,const DeepCollectionEquality().hash(_links),lastSeenAt,activeBadge,experience,level,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt]);
@override @override
String toString() { String toString() {
return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, links: $links, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
} }
@@ -664,7 +672,7 @@ abstract mixin class _$SnAccountProfileCopyWith<$Res> implements $SnAccountProfi
factory _$SnAccountProfileCopyWith(_SnAccountProfile value, $Res Function(_SnAccountProfile) _then) = __$SnAccountProfileCopyWithImpl; factory _$SnAccountProfileCopyWith(_SnAccountProfile value, $Res Function(_SnAccountProfile) _then) = __$SnAccountProfileCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, Map<String, String> links, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
}); });
@@ -681,7 +689,7 @@ class __$SnAccountProfileCopyWithImpl<$Res>
/// Create a copy of SnAccountProfile /// Create a copy of SnAccountProfile
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? links = null,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
return _then(_SnAccountProfile( return _then(_SnAccountProfile(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
@@ -693,7 +701,8 @@ as String,pronouns: null == pronouns ? _self.pronouns : pronouns // ignore: cast
as String,location: null == location ? _self.location : location // ignore: cast_nullable_to_non_nullable as String,location: null == location ? _self.location : location // ignore: cast_nullable_to_non_nullable
as String,timeZone: null == timeZone ? _self.timeZone : timeZone // ignore: cast_nullable_to_non_nullable as String,timeZone: null == timeZone ? _self.timeZone : timeZone // ignore: cast_nullable_to_non_nullable
as String,birthday: freezed == birthday ? _self.birthday : birthday // ignore: cast_nullable_to_non_nullable as String,birthday: freezed == birthday ? _self.birthday : birthday // ignore: cast_nullable_to_non_nullable
as DateTime?,lastSeenAt: freezed == lastSeenAt ? _self.lastSeenAt : lastSeenAt // ignore: cast_nullable_to_non_nullable as DateTime?,links: null == links ? _self._links : links // ignore: cast_nullable_to_non_nullable
as Map<String, String>,lastSeenAt: freezed == lastSeenAt ? _self.lastSeenAt : lastSeenAt // ignore: cast_nullable_to_non_nullable
as DateTime?,activeBadge: freezed == activeBadge ? _self.activeBadge : activeBadge // ignore: cast_nullable_to_non_nullable as DateTime?,activeBadge: freezed == activeBadge ? _self.activeBadge : activeBadge // ignore: cast_nullable_to_non_nullable
as SnAccountBadge?,experience: null == experience ? _self.experience : experience // ignore: cast_nullable_to_non_nullable as SnAccountBadge?,experience: null == experience ? _self.experience : experience // ignore: cast_nullable_to_non_nullable
as int,level: null == level ? _self.level : level // ignore: cast_nullable_to_non_nullable as int,level: null == level ? _self.level : level // ignore: cast_nullable_to_non_nullable

View File

@@ -62,6 +62,11 @@ _SnAccountProfile _$SnAccountProfileFromJson(Map<String, dynamic> json) =>
json['birthday'] == null json['birthday'] == null
? null ? null
: DateTime.parse(json['birthday'] as String), : DateTime.parse(json['birthday'] as String),
links:
(json['links'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
) ??
const {},
lastSeenAt: lastSeenAt:
json['last_seen_at'] == null json['last_seen_at'] == null
? null ? null
@@ -111,6 +116,7 @@ Map<String, dynamic> _$SnAccountProfileToJson(_SnAccountProfile instance) =>
'location': instance.location, 'location': instance.location,
'time_zone': instance.timeZone, 'time_zone': instance.timeZone,
'birthday': instance.birthday?.toIso8601String(), 'birthday': instance.birthday?.toIso8601String(),
'links': instance.links,
'last_seen_at': instance.lastSeenAt?.toIso8601String(), 'last_seen_at': instance.lastSeenAt?.toIso8601String(),
'active_badge': instance.activeBadge?.toJson(), 'active_badge': instance.activeBadge?.toJson(),
'experience': instance.experience, 'experience': instance.experience,

View File

@@ -1,13 +1,14 @@
import 'package:island/pods/userinfo.dart'; import 'dart:async';
import 'package:island/screens/chat/chat.dart'; import 'dart:developer';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:island/widgets/chat/call_button.dart'; import 'package:island/widgets/chat/call_button.dart';
import 'package:livekit_client/livekit_client.dart'; import 'package:livekit_client/livekit_client.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/models/chat.dart'; import 'package:island/models/chat.dart';
import 'package:island/pods/websocket.dart';
part 'call.g.dart'; part 'call.g.dart';
part 'call.freezed.dart'; part 'call.freezed.dart';
@@ -27,6 +28,7 @@ sealed class CallState with _$CallState {
required bool isMicrophoneEnabled, required bool isMicrophoneEnabled,
required bool isCameraEnabled, required bool isCameraEnabled,
required bool isScreenSharing, required bool isScreenSharing,
required bool isSpeakerphone,
@Default(Duration(seconds: 0)) Duration duration, @Default(Duration(seconds: 0)) Duration duration,
String? error, String? error,
}) = _CallState; }) = _CallState;
@@ -42,7 +44,8 @@ sealed class CallParticipantLive with _$CallParticipantLive {
}) = _CallParticipantLive; }) = _CallParticipantLive;
bool get isSpeaking => remoteParticipant.isSpeaking; bool get isSpeaking => remoteParticipant.isSpeaking;
bool get isMuted => remoteParticipant.isMuted; bool get isMuted =>
remoteParticipant.isMuted || !remoteParticipant.isMicrophoneEnabled();
bool get isScreenSharing => remoteParticipant.isScreenShareEnabled(); bool get isScreenSharing => remoteParticipant.isScreenShareEnabled();
bool get isScreenSharingWithAudio => bool get isScreenSharingWithAudio =>
remoteParticipant.isScreenShareAudioEnabled(); remoteParticipant.isScreenShareAudioEnabled();
@@ -57,13 +60,14 @@ class CallNotifier extends _$CallNotifier {
LocalParticipant? _localParticipant; LocalParticipant? _localParticipant;
List<CallParticipantLive> _participants = []; List<CallParticipantLive> _participants = [];
final Map<String, CallParticipant> _participantInfoByIdentity = {}; final Map<String, CallParticipant> _participantInfoByIdentity = {};
StreamSubscription? _wsSubscription;
EventsListener? _roomListener; EventsListener? _roomListener;
List<CallParticipantLive> get participants => List<CallParticipantLive> get participants =>
List.unmodifiable(_participants); List.unmodifiable(_participants);
LocalParticipant? get localParticipant => _localParticipant; LocalParticipant? get localParticipant => _localParticipant;
Map<String, double> participantsVolumes = {};
Timer? _durationTimer; Timer? _durationTimer;
Room? get room => _room; Room? get room => _room;
@@ -71,36 +75,15 @@ class CallNotifier extends _$CallNotifier {
@override @override
CallState build() { CallState build() {
// Subscribe to websocket updates // Subscribe to websocket updates
_subscribeToParticipantsUpdate();
return const CallState( return const CallState(
isConnected: false, isConnected: false,
isMicrophoneEnabled: true, isMicrophoneEnabled: true,
isCameraEnabled: false, isCameraEnabled: false,
isScreenSharing: false, isScreenSharing: false,
isSpeakerphone: true,
); );
} }
void _subscribeToParticipantsUpdate() {
// Only subscribe once
if (_wsSubscription != null) return;
final ws = ref.read(websocketProvider);
_wsSubscription = ws.dataStream.listen((packet) {
if (packet.type == 'call.participants.update' && packet.data != null) {
final participantsData = packet.data!["participants"];
if (participantsData is List) {
final parsed =
participantsData
.map(
(e) =>
CallParticipant.fromJson(Map<String, dynamic>.from(e)),
)
.toList();
_updateLiveParticipants(parsed);
}
}
});
}
void _initRoomListeners() { void _initRoomListeners() {
if (_room == null) return; if (_room == null) return;
_roomListener?.dispose(); _roomListener?.dispose();
@@ -143,8 +126,6 @@ class CallNotifier extends _$CallNotifier {
identity: remote.identity, identity: remote.identity,
name: remote.identity, name: remote.identity,
joinedAt: DateTime.now(), joinedAt: DateTime.now(),
accountId: null,
profile: null,
); );
return CallParticipantLive( return CallParticipantLive(
participant: match, participant: match,
@@ -169,16 +150,12 @@ class CallNotifier extends _$CallNotifier {
if (idx != -1) return participants[idx]; if (idx != -1) return participants[idx];
} }
final userInfo = ref.read(userInfoProvider);
final roomIdentity = ref.read(chatroomIdentityProvider(_roomId));
// Otherwise, use info from the identity map or fallback to minimal // Otherwise, use info from the identity map or fallback to minimal
return _participantInfoByIdentity[_localParticipant!.identity] ?? return _participantInfoByIdentity[_localParticipant!.identity] ??
CallParticipant( CallParticipant(
identity: _localParticipant!.identity, identity: _localParticipant!.identity,
name: _localParticipant!.identity, name: _localParticipant!.identity,
joinedAt: DateTime.now(), joinedAt: DateTime.now(),
accountId: userInfo.value?.id,
profile: roomIdentity.value,
); );
} }
@@ -205,6 +182,7 @@ class CallNotifier extends _$CallNotifier {
remoteParticipant: _localParticipant!, remoteParticipant: _localParticipant!,
), ),
); );
state = state.copyWith();
} }
// Add remote participants // Add remote participants
_participants.addAll( _participants.addAll(
@@ -233,7 +211,13 @@ class CallNotifier extends _$CallNotifier {
Future<void> joinRoom(String roomId) async { Future<void> joinRoom(String roomId) async {
if (_roomId == roomId && _room != null) { if (_roomId == roomId && _room != null) {
log('[Call] Call skipped. Already has data');
return; return;
} else if (_room != null) {
if (!_room!.isDisposed &&
_room!.connectionState != ConnectionState.disconnected) {
throw Exception('Call already connected');
}
} }
_roomId = roomId; _roomId = roomId;
if (_room != null) { if (_room != null) {
@@ -264,7 +248,8 @@ class CallNotifier extends _$CallNotifier {
duration: Duration( duration: Duration(
milliseconds: milliseconds:
(DateTime.now().millisecondsSinceEpoch - (DateTime.now().millisecondsSinceEpoch -
(ongoingCall?.createdAt.millisecondsSinceEpoch ?? 0)), (ongoingCall?.createdAt.millisecondsSinceEpoch ??
DateTime.now().millisecondsSinceEpoch)),
), ),
); );
}); });
@@ -286,6 +271,10 @@ class CallNotifier extends _$CallNotifier {
_initRoomListeners(); _initRoomListeners();
_updateLiveParticipants(participants); _updateLiveParticipants(participants);
if (!kIsWeb && (Platform.isIOS || Platform.isAndroid)) {
Hardware.instance.setSpeakerphoneOn(true);
}
// Listen for connection updates // Listen for connection updates
_room!.addListener(() { _room!.addListener(() {
state = state.copyWith( state = state.copyWith(
@@ -318,6 +307,7 @@ class CallNotifier extends _$CallNotifier {
stopOnMute: autostop, stopOnMute: autostop,
); );
} }
state = state.copyWith();
} }
} }
@@ -326,6 +316,7 @@ class CallNotifier extends _$CallNotifier {
final target = !_localParticipant!.isCameraEnabled(); final target = !_localParticipant!.isCameraEnabled();
state = state.copyWith(isCameraEnabled: target); state = state.copyWith(isCameraEnabled: target);
await _localParticipant!.setCameraEnabled(target); await _localParticipant!.setCameraEnabled(target);
state = state.copyWith();
} }
} }
@@ -334,9 +325,16 @@ class CallNotifier extends _$CallNotifier {
final target = !_localParticipant!.isScreenShareEnabled(); final target = !_localParticipant!.isScreenShareEnabled();
state = state.copyWith(isScreenSharing: target); state = state.copyWith(isScreenSharing: target);
await _localParticipant!.setScreenShareEnabled(target); await _localParticipant!.setScreenShareEnabled(target);
state = state.copyWith();
} }
} }
Future<void> toggleSpeakerphone() async {
state = state.copyWith(isSpeakerphone: !state.isSpeakerphone);
await Hardware.instance.setSpeakerphoneOn(state.isSpeakerphone);
state = state.copyWith();
}
Future<void> disconnect() async { Future<void> disconnect() async {
if (_room != null) { if (_room != null) {
await _room!.disconnect(); await _room!.disconnect();
@@ -349,11 +347,39 @@ class CallNotifier extends _$CallNotifier {
} }
} }
void setParticipantVolume(CallParticipantLive live, double volume) {
if (participantsVolumes[live.remoteParticipant.sid] == null) {
participantsVolumes[live.remoteParticipant.sid] = 1;
}
Helper.setVolume(
volume,
live
.remoteParticipant
.audioTrackPublications
.first
.track!
.mediaStreamTrack,
);
participantsVolumes[live.remoteParticipant.sid] = volume;
}
double getParticipantVolume(CallParticipantLive live) {
return participantsVolumes[live.remoteParticipant.sid] ?? 1;
}
void dispose() { void dispose() {
_wsSubscription?.cancel(); state = state.copyWith(
error: null,
isConnected: false,
isMicrophoneEnabled: false,
isCameraEnabled: false,
isScreenSharing: false,
);
_roomListener?.dispose(); _roomListener?.dispose();
_room?.removeListener(_onRoomChange); _room?.removeListener(_onRoomChange);
_room?.dispose(); _room?.dispose();
_durationTimer?.cancel(); _durationTimer?.cancel();
_roomId = null;
participantsVolumes = {};
} }
} }

View File

@@ -12,9 +12,9 @@ part of 'call.dart';
// dart format off // dart format off
T _$identity<T>(T value) => value; T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$CallState { mixin _$CallState implements DiagnosticableTreeMixin {
bool get isConnected; bool get isMicrophoneEnabled; bool get isCameraEnabled; bool get isScreenSharing; Duration get duration; String? get error; bool get isConnected; bool get isMicrophoneEnabled; bool get isCameraEnabled; bool get isScreenSharing; bool get isSpeakerphone; Duration get duration; String? get error;
/// Create a copy of CallState /// Create a copy of CallState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -22,19 +22,25 @@ mixin _$CallState {
$CallStateCopyWith<CallState> get copyWith => _$CallStateCopyWithImpl<CallState>(this as CallState, _$identity); $CallStateCopyWith<CallState> get copyWith => _$CallStateCopyWithImpl<CallState>(this as CallState, _$identity);
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
properties
..add(DiagnosticsProperty('type', 'CallState'))
..add(DiagnosticsProperty('isConnected', isConnected))..add(DiagnosticsProperty('isMicrophoneEnabled', isMicrophoneEnabled))..add(DiagnosticsProperty('isCameraEnabled', isCameraEnabled))..add(DiagnosticsProperty('isScreenSharing', isScreenSharing))..add(DiagnosticsProperty('isSpeakerphone', isSpeakerphone))..add(DiagnosticsProperty('duration', duration))..add(DiagnosticsProperty('error', error));
}
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is CallState&&(identical(other.isConnected, isConnected) || other.isConnected == isConnected)&&(identical(other.isMicrophoneEnabled, isMicrophoneEnabled) || other.isMicrophoneEnabled == isMicrophoneEnabled)&&(identical(other.isCameraEnabled, isCameraEnabled) || other.isCameraEnabled == isCameraEnabled)&&(identical(other.isScreenSharing, isScreenSharing) || other.isScreenSharing == isScreenSharing)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.error, error) || other.error == error)); return identical(this, other) || (other.runtimeType == runtimeType&&other is CallState&&(identical(other.isConnected, isConnected) || other.isConnected == isConnected)&&(identical(other.isMicrophoneEnabled, isMicrophoneEnabled) || other.isMicrophoneEnabled == isMicrophoneEnabled)&&(identical(other.isCameraEnabled, isCameraEnabled) || other.isCameraEnabled == isCameraEnabled)&&(identical(other.isScreenSharing, isScreenSharing) || other.isScreenSharing == isScreenSharing)&&(identical(other.isSpeakerphone, isSpeakerphone) || other.isSpeakerphone == isSpeakerphone)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.error, error) || other.error == error));
} }
@override @override
int get hashCode => Object.hash(runtimeType,isConnected,isMicrophoneEnabled,isCameraEnabled,isScreenSharing,duration,error); int get hashCode => Object.hash(runtimeType,isConnected,isMicrophoneEnabled,isCameraEnabled,isScreenSharing,isSpeakerphone,duration,error);
@override @override
String toString() { String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
return 'CallState(isConnected: $isConnected, isMicrophoneEnabled: $isMicrophoneEnabled, isCameraEnabled: $isCameraEnabled, isScreenSharing: $isScreenSharing, duration: $duration, error: $error)'; return 'CallState(isConnected: $isConnected, isMicrophoneEnabled: $isMicrophoneEnabled, isCameraEnabled: $isCameraEnabled, isScreenSharing: $isScreenSharing, isSpeakerphone: $isSpeakerphone, duration: $duration, error: $error)';
} }
@@ -45,7 +51,7 @@ abstract mixin class $CallStateCopyWith<$Res> {
factory $CallStateCopyWith(CallState value, $Res Function(CallState) _then) = _$CallStateCopyWithImpl; factory $CallStateCopyWith(CallState value, $Res Function(CallState) _then) = _$CallStateCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
bool isConnected, bool isMicrophoneEnabled, bool isCameraEnabled, bool isScreenSharing, Duration duration, String? error bool isConnected, bool isMicrophoneEnabled, bool isCameraEnabled, bool isScreenSharing, bool isSpeakerphone, Duration duration, String? error
}); });
@@ -62,12 +68,13 @@ class _$CallStateCopyWithImpl<$Res>
/// Create a copy of CallState /// Create a copy of CallState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isConnected = null,Object? isMicrophoneEnabled = null,Object? isCameraEnabled = null,Object? isScreenSharing = null,Object? duration = null,Object? error = freezed,}) { @pragma('vm:prefer-inline') @override $Res call({Object? isConnected = null,Object? isMicrophoneEnabled = null,Object? isCameraEnabled = null,Object? isScreenSharing = null,Object? isSpeakerphone = null,Object? duration = null,Object? error = freezed,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
isConnected: null == isConnected ? _self.isConnected : isConnected // ignore: cast_nullable_to_non_nullable isConnected: null == isConnected ? _self.isConnected : isConnected // ignore: cast_nullable_to_non_nullable
as bool,isMicrophoneEnabled: null == isMicrophoneEnabled ? _self.isMicrophoneEnabled : isMicrophoneEnabled // ignore: cast_nullable_to_non_nullable as bool,isMicrophoneEnabled: null == isMicrophoneEnabled ? _self.isMicrophoneEnabled : isMicrophoneEnabled // ignore: cast_nullable_to_non_nullable
as bool,isCameraEnabled: null == isCameraEnabled ? _self.isCameraEnabled : isCameraEnabled // ignore: cast_nullable_to_non_nullable as bool,isCameraEnabled: null == isCameraEnabled ? _self.isCameraEnabled : isCameraEnabled // ignore: cast_nullable_to_non_nullable
as bool,isScreenSharing: null == isScreenSharing ? _self.isScreenSharing : isScreenSharing // ignore: cast_nullable_to_non_nullable as bool,isScreenSharing: null == isScreenSharing ? _self.isScreenSharing : isScreenSharing // ignore: cast_nullable_to_non_nullable
as bool,isSpeakerphone: null == isSpeakerphone ? _self.isSpeakerphone : isSpeakerphone // ignore: cast_nullable_to_non_nullable
as bool,duration: null == duration ? _self.duration : duration // ignore: cast_nullable_to_non_nullable as bool,duration: null == duration ? _self.duration : duration // ignore: cast_nullable_to_non_nullable
as Duration,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable as Duration,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
as String?, as String?,
@@ -152,10 +159,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isConnected, bool isMicrophoneEnabled, bool isCameraEnabled, bool isScreenSharing, Duration duration, String? error)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isConnected, bool isMicrophoneEnabled, bool isCameraEnabled, bool isScreenSharing, bool isSpeakerphone, Duration duration, String? error)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _CallState() when $default != null: case _CallState() when $default != null:
return $default(_that.isConnected,_that.isMicrophoneEnabled,_that.isCameraEnabled,_that.isScreenSharing,_that.duration,_that.error);case _: return $default(_that.isConnected,_that.isMicrophoneEnabled,_that.isCameraEnabled,_that.isScreenSharing,_that.isSpeakerphone,_that.duration,_that.error);case _:
return orElse(); return orElse();
} }
@@ -173,10 +180,10 @@ return $default(_that.isConnected,_that.isMicrophoneEnabled,_that.isCameraEnable
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isConnected, bool isMicrophoneEnabled, bool isCameraEnabled, bool isScreenSharing, Duration duration, String? error) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isConnected, bool isMicrophoneEnabled, bool isCameraEnabled, bool isScreenSharing, bool isSpeakerphone, Duration duration, String? error) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _CallState(): case _CallState():
return $default(_that.isConnected,_that.isMicrophoneEnabled,_that.isCameraEnabled,_that.isScreenSharing,_that.duration,_that.error);} return $default(_that.isConnected,_that.isMicrophoneEnabled,_that.isCameraEnabled,_that.isScreenSharing,_that.isSpeakerphone,_that.duration,_that.error);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
/// ///
@@ -190,10 +197,10 @@ return $default(_that.isConnected,_that.isMicrophoneEnabled,_that.isCameraEnable
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isConnected, bool isMicrophoneEnabled, bool isCameraEnabled, bool isScreenSharing, Duration duration, String? error)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isConnected, bool isMicrophoneEnabled, bool isCameraEnabled, bool isScreenSharing, bool isSpeakerphone, Duration duration, String? error)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _CallState() when $default != null: case _CallState() when $default != null:
return $default(_that.isConnected,_that.isMicrophoneEnabled,_that.isCameraEnabled,_that.isScreenSharing,_that.duration,_that.error);case _: return $default(_that.isConnected,_that.isMicrophoneEnabled,_that.isCameraEnabled,_that.isScreenSharing,_that.isSpeakerphone,_that.duration,_that.error);case _:
return null; return null;
} }
@@ -204,14 +211,15 @@ return $default(_that.isConnected,_that.isMicrophoneEnabled,_that.isCameraEnable
/// @nodoc /// @nodoc
class _CallState implements CallState { class _CallState with DiagnosticableTreeMixin implements CallState {
const _CallState({required this.isConnected, required this.isMicrophoneEnabled, required this.isCameraEnabled, required this.isScreenSharing, this.duration = const Duration(seconds: 0), this.error}); const _CallState({required this.isConnected, required this.isMicrophoneEnabled, required this.isCameraEnabled, required this.isScreenSharing, required this.isSpeakerphone, this.duration = const Duration(seconds: 0), this.error});
@override final bool isConnected; @override final bool isConnected;
@override final bool isMicrophoneEnabled; @override final bool isMicrophoneEnabled;
@override final bool isCameraEnabled; @override final bool isCameraEnabled;
@override final bool isScreenSharing; @override final bool isScreenSharing;
@override final bool isSpeakerphone;
@override@JsonKey() final Duration duration; @override@JsonKey() final Duration duration;
@override final String? error; @override final String? error;
@@ -222,19 +230,25 @@ class _CallState implements CallState {
_$CallStateCopyWith<_CallState> get copyWith => __$CallStateCopyWithImpl<_CallState>(this, _$identity); _$CallStateCopyWith<_CallState> get copyWith => __$CallStateCopyWithImpl<_CallState>(this, _$identity);
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
properties
..add(DiagnosticsProperty('type', 'CallState'))
..add(DiagnosticsProperty('isConnected', isConnected))..add(DiagnosticsProperty('isMicrophoneEnabled', isMicrophoneEnabled))..add(DiagnosticsProperty('isCameraEnabled', isCameraEnabled))..add(DiagnosticsProperty('isScreenSharing', isScreenSharing))..add(DiagnosticsProperty('isSpeakerphone', isSpeakerphone))..add(DiagnosticsProperty('duration', duration))..add(DiagnosticsProperty('error', error));
}
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallState&&(identical(other.isConnected, isConnected) || other.isConnected == isConnected)&&(identical(other.isMicrophoneEnabled, isMicrophoneEnabled) || other.isMicrophoneEnabled == isMicrophoneEnabled)&&(identical(other.isCameraEnabled, isCameraEnabled) || other.isCameraEnabled == isCameraEnabled)&&(identical(other.isScreenSharing, isScreenSharing) || other.isScreenSharing == isScreenSharing)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.error, error) || other.error == error)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallState&&(identical(other.isConnected, isConnected) || other.isConnected == isConnected)&&(identical(other.isMicrophoneEnabled, isMicrophoneEnabled) || other.isMicrophoneEnabled == isMicrophoneEnabled)&&(identical(other.isCameraEnabled, isCameraEnabled) || other.isCameraEnabled == isCameraEnabled)&&(identical(other.isScreenSharing, isScreenSharing) || other.isScreenSharing == isScreenSharing)&&(identical(other.isSpeakerphone, isSpeakerphone) || other.isSpeakerphone == isSpeakerphone)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.error, error) || other.error == error));
} }
@override @override
int get hashCode => Object.hash(runtimeType,isConnected,isMicrophoneEnabled,isCameraEnabled,isScreenSharing,duration,error); int get hashCode => Object.hash(runtimeType,isConnected,isMicrophoneEnabled,isCameraEnabled,isScreenSharing,isSpeakerphone,duration,error);
@override @override
String toString() { String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
return 'CallState(isConnected: $isConnected, isMicrophoneEnabled: $isMicrophoneEnabled, isCameraEnabled: $isCameraEnabled, isScreenSharing: $isScreenSharing, duration: $duration, error: $error)'; return 'CallState(isConnected: $isConnected, isMicrophoneEnabled: $isMicrophoneEnabled, isCameraEnabled: $isCameraEnabled, isScreenSharing: $isScreenSharing, isSpeakerphone: $isSpeakerphone, duration: $duration, error: $error)';
} }
@@ -245,7 +259,7 @@ abstract mixin class _$CallStateCopyWith<$Res> implements $CallStateCopyWith<$Re
factory _$CallStateCopyWith(_CallState value, $Res Function(_CallState) _then) = __$CallStateCopyWithImpl; factory _$CallStateCopyWith(_CallState value, $Res Function(_CallState) _then) = __$CallStateCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
bool isConnected, bool isMicrophoneEnabled, bool isCameraEnabled, bool isScreenSharing, Duration duration, String? error bool isConnected, bool isMicrophoneEnabled, bool isCameraEnabled, bool isScreenSharing, bool isSpeakerphone, Duration duration, String? error
}); });
@@ -262,12 +276,13 @@ class __$CallStateCopyWithImpl<$Res>
/// Create a copy of CallState /// Create a copy of CallState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isConnected = null,Object? isMicrophoneEnabled = null,Object? isCameraEnabled = null,Object? isScreenSharing = null,Object? duration = null,Object? error = freezed,}) { @override @pragma('vm:prefer-inline') $Res call({Object? isConnected = null,Object? isMicrophoneEnabled = null,Object? isCameraEnabled = null,Object? isScreenSharing = null,Object? isSpeakerphone = null,Object? duration = null,Object? error = freezed,}) {
return _then(_CallState( return _then(_CallState(
isConnected: null == isConnected ? _self.isConnected : isConnected // ignore: cast_nullable_to_non_nullable isConnected: null == isConnected ? _self.isConnected : isConnected // ignore: cast_nullable_to_non_nullable
as bool,isMicrophoneEnabled: null == isMicrophoneEnabled ? _self.isMicrophoneEnabled : isMicrophoneEnabled // ignore: cast_nullable_to_non_nullable as bool,isMicrophoneEnabled: null == isMicrophoneEnabled ? _self.isMicrophoneEnabled : isMicrophoneEnabled // ignore: cast_nullable_to_non_nullable
as bool,isCameraEnabled: null == isCameraEnabled ? _self.isCameraEnabled : isCameraEnabled // ignore: cast_nullable_to_non_nullable as bool,isCameraEnabled: null == isCameraEnabled ? _self.isCameraEnabled : isCameraEnabled // ignore: cast_nullable_to_non_nullable
as bool,isScreenSharing: null == isScreenSharing ? _self.isScreenSharing : isScreenSharing // ignore: cast_nullable_to_non_nullable as bool,isScreenSharing: null == isScreenSharing ? _self.isScreenSharing : isScreenSharing // ignore: cast_nullable_to_non_nullable
as bool,isSpeakerphone: null == isSpeakerphone ? _self.isSpeakerphone : isSpeakerphone // ignore: cast_nullable_to_non_nullable
as bool,duration: null == duration ? _self.duration : duration // ignore: cast_nullable_to_non_nullable as bool,duration: null == duration ? _self.duration : duration // ignore: cast_nullable_to_non_nullable
as Duration,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable as Duration,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
as String?, as String?,
@@ -278,7 +293,7 @@ as String?,
} }
/// @nodoc /// @nodoc
mixin _$CallParticipantLive { mixin _$CallParticipantLive implements DiagnosticableTreeMixin {
CallParticipant get participant; Participant get remoteParticipant; CallParticipant get participant; Participant get remoteParticipant;
/// Create a copy of CallParticipantLive /// Create a copy of CallParticipantLive
@@ -288,6 +303,12 @@ mixin _$CallParticipantLive {
$CallParticipantLiveCopyWith<CallParticipantLive> get copyWith => _$CallParticipantLiveCopyWithImpl<CallParticipantLive>(this as CallParticipantLive, _$identity); $CallParticipantLiveCopyWith<CallParticipantLive> get copyWith => _$CallParticipantLiveCopyWithImpl<CallParticipantLive>(this as CallParticipantLive, _$identity);
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
properties
..add(DiagnosticsProperty('type', 'CallParticipantLive'))
..add(DiagnosticsProperty('participant', participant))..add(DiagnosticsProperty('remoteParticipant', remoteParticipant));
}
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
@@ -299,7 +320,7 @@ bool operator ==(Object other) {
int get hashCode => Object.hash(runtimeType,participant,remoteParticipant); int get hashCode => Object.hash(runtimeType,participant,remoteParticipant);
@override @override
String toString() { String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
return 'CallParticipantLive(participant: $participant, remoteParticipant: $remoteParticipant)'; return 'CallParticipantLive(participant: $participant, remoteParticipant: $remoteParticipant)';
} }
@@ -475,7 +496,7 @@ return $default(_that.participant,_that.remoteParticipant);case _:
/// @nodoc /// @nodoc
class _CallParticipantLive extends CallParticipantLive { class _CallParticipantLive extends CallParticipantLive with DiagnosticableTreeMixin {
const _CallParticipantLive({required this.participant, required this.remoteParticipant}): super._(); const _CallParticipantLive({required this.participant, required this.remoteParticipant}): super._();
@@ -489,6 +510,12 @@ class _CallParticipantLive extends CallParticipantLive {
_$CallParticipantLiveCopyWith<_CallParticipantLive> get copyWith => __$CallParticipantLiveCopyWithImpl<_CallParticipantLive>(this, _$identity); _$CallParticipantLiveCopyWith<_CallParticipantLive> get copyWith => __$CallParticipantLiveCopyWithImpl<_CallParticipantLive>(this, _$identity);
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
properties
..add(DiagnosticsProperty('type', 'CallParticipantLive'))
..add(DiagnosticsProperty('participant', participant))..add(DiagnosticsProperty('remoteParticipant', remoteParticipant));
}
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
@@ -500,7 +527,7 @@ bool operator ==(Object other) {
int get hashCode => Object.hash(runtimeType,participant,remoteParticipant); int get hashCode => Object.hash(runtimeType,participant,remoteParticipant);
@override @override
String toString() { String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
return 'CallParticipantLive(participant: $participant, remoteParticipant: $remoteParticipant)'; return 'CallParticipantLive(participant: $participant, remoteParticipant: $remoteParticipant)';
} }

View File

@@ -6,7 +6,7 @@ part of 'call.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$callNotifierHash() => r'107174cd6cfab6bfafe44f8c4a72a67bcb93217b'; String _$callNotifierHash() => r'18fb807f067eecd3ea42631c1426c3e5f1fb4280';
/// See also [CallNotifier]. /// See also [CallNotifier].
@ProviderFor(CallNotifier) @ProviderFor(CallNotifier)

38
lib/pods/translate.dart Normal file
View File

@@ -0,0 +1,38 @@
import 'dart:convert';
import 'dart:developer';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/network.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:flutter_langdetect/flutter_langdetect.dart' as langdetect;
part 'translate.freezed.dart';
part 'translate.g.dart';
@freezed
sealed class TranslateQuery with _$TranslateQuery {
const factory TranslateQuery({required String text, required String lang}) =
_TranslateQuery;
}
@riverpod
Future<String> translateString(Ref ref, TranslateQuery query) async {
final client = ref.watch(apiClientProvider);
final response = await client.post(
'/sphere/translate',
queryParameters: {'to': query.lang},
data: jsonEncode(query.text),
);
return response.data as String;
}
@riverpod
String? detectStringLanguage(Ref ref, String text) {
try {
return langdetect.detectLangs(text).firstOrNull?.lang;
} catch (err) {
log('[Language] Unable to detect text\'s language: $text');
return null;
}
}

View File

@@ -0,0 +1,268 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'translate.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$TranslateQuery {
String get text; String get lang;
/// Create a copy of TranslateQuery
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$TranslateQueryCopyWith<TranslateQuery> get copyWith => _$TranslateQueryCopyWithImpl<TranslateQuery>(this as TranslateQuery, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TranslateQuery&&(identical(other.text, text) || other.text == text)&&(identical(other.lang, lang) || other.lang == lang));
}
@override
int get hashCode => Object.hash(runtimeType,text,lang);
@override
String toString() {
return 'TranslateQuery(text: $text, lang: $lang)';
}
}
/// @nodoc
abstract mixin class $TranslateQueryCopyWith<$Res> {
factory $TranslateQueryCopyWith(TranslateQuery value, $Res Function(TranslateQuery) _then) = _$TranslateQueryCopyWithImpl;
@useResult
$Res call({
String text, String lang
});
}
/// @nodoc
class _$TranslateQueryCopyWithImpl<$Res>
implements $TranslateQueryCopyWith<$Res> {
_$TranslateQueryCopyWithImpl(this._self, this._then);
final TranslateQuery _self;
final $Res Function(TranslateQuery) _then;
/// Create a copy of TranslateQuery
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? text = null,Object? lang = null,}) {
return _then(_self.copyWith(
text: null == text ? _self.text : text // ignore: cast_nullable_to_non_nullable
as String,lang: null == lang ? _self.lang : lang // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [TranslateQuery].
extension TranslateQueryPatterns on TranslateQuery {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _TranslateQuery value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _TranslateQuery() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _TranslateQuery value) $default,){
final _that = this;
switch (_that) {
case _TranslateQuery():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _TranslateQuery value)? $default,){
final _that = this;
switch (_that) {
case _TranslateQuery() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String text, String lang)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _TranslateQuery() when $default != null:
return $default(_that.text,_that.lang);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String text, String lang) $default,) {final _that = this;
switch (_that) {
case _TranslateQuery():
return $default(_that.text,_that.lang);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String text, String lang)? $default,) {final _that = this;
switch (_that) {
case _TranslateQuery() when $default != null:
return $default(_that.text,_that.lang);case _:
return null;
}
}
}
/// @nodoc
class _TranslateQuery implements TranslateQuery {
const _TranslateQuery({required this.text, required this.lang});
@override final String text;
@override final String lang;
/// Create a copy of TranslateQuery
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$TranslateQueryCopyWith<_TranslateQuery> get copyWith => __$TranslateQueryCopyWithImpl<_TranslateQuery>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _TranslateQuery&&(identical(other.text, text) || other.text == text)&&(identical(other.lang, lang) || other.lang == lang));
}
@override
int get hashCode => Object.hash(runtimeType,text,lang);
@override
String toString() {
return 'TranslateQuery(text: $text, lang: $lang)';
}
}
/// @nodoc
abstract mixin class _$TranslateQueryCopyWith<$Res> implements $TranslateQueryCopyWith<$Res> {
factory _$TranslateQueryCopyWith(_TranslateQuery value, $Res Function(_TranslateQuery) _then) = __$TranslateQueryCopyWithImpl;
@override @useResult
$Res call({
String text, String lang
});
}
/// @nodoc
class __$TranslateQueryCopyWithImpl<$Res>
implements _$TranslateQueryCopyWith<$Res> {
__$TranslateQueryCopyWithImpl(this._self, this._then);
final _TranslateQuery _self;
final $Res Function(_TranslateQuery) _then;
/// Create a copy of TranslateQuery
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? text = null,Object? lang = null,}) {
return _then(_TranslateQuery(
text: null == text ? _self.text : text // ignore: cast_nullable_to_non_nullable
as String,lang: null == lang ? _self.lang : lang // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
// dart format on

274
lib/pods/translate.g.dart Normal file
View File

@@ -0,0 +1,274 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'translate.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$translateStringHash() => r'51d638cf07cbf3ffa9469298f5bd9c667bc0ccb7';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [translateString].
@ProviderFor(translateString)
const translateStringProvider = TranslateStringFamily();
/// See also [translateString].
class TranslateStringFamily extends Family<AsyncValue<String>> {
/// See also [translateString].
const TranslateStringFamily();
/// See also [translateString].
TranslateStringProvider call(TranslateQuery query) {
return TranslateStringProvider(query);
}
@override
TranslateStringProvider getProviderOverride(
covariant TranslateStringProvider provider,
) {
return call(provider.query);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'translateStringProvider';
}
/// See also [translateString].
class TranslateStringProvider extends AutoDisposeFutureProvider<String> {
/// See also [translateString].
TranslateStringProvider(TranslateQuery query)
: this._internal(
(ref) => translateString(ref as TranslateStringRef, query),
from: translateStringProvider,
name: r'translateStringProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$translateStringHash,
dependencies: TranslateStringFamily._dependencies,
allTransitiveDependencies:
TranslateStringFamily._allTransitiveDependencies,
query: query,
);
TranslateStringProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.query,
}) : super.internal();
final TranslateQuery query;
@override
Override overrideWith(
FutureOr<String> Function(TranslateStringRef provider) create,
) {
return ProviderOverride(
origin: this,
override: TranslateStringProvider._internal(
(ref) => create(ref as TranslateStringRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
query: query,
),
);
}
@override
AutoDisposeFutureProviderElement<String> createElement() {
return _TranslateStringProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is TranslateStringProvider && other.query == query;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, query.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin TranslateStringRef on AutoDisposeFutureProviderRef<String> {
/// The parameter `query` of this provider.
TranslateQuery get query;
}
class _TranslateStringProviderElement
extends AutoDisposeFutureProviderElement<String>
with TranslateStringRef {
_TranslateStringProviderElement(super.provider);
@override
TranslateQuery get query => (origin as TranslateStringProvider).query;
}
String _$detectStringLanguageHash() =>
r'697b68464b3d00927cc43ccc1ba8ba93f2a470ed';
/// See also [detectStringLanguage].
@ProviderFor(detectStringLanguage)
const detectStringLanguageProvider = DetectStringLanguageFamily();
/// See also [detectStringLanguage].
class DetectStringLanguageFamily extends Family<String?> {
/// See also [detectStringLanguage].
const DetectStringLanguageFamily();
/// See also [detectStringLanguage].
DetectStringLanguageProvider call(String text) {
return DetectStringLanguageProvider(text);
}
@override
DetectStringLanguageProvider getProviderOverride(
covariant DetectStringLanguageProvider provider,
) {
return call(provider.text);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'detectStringLanguageProvider';
}
/// See also [detectStringLanguage].
class DetectStringLanguageProvider extends AutoDisposeProvider<String?> {
/// See also [detectStringLanguage].
DetectStringLanguageProvider(String text)
: this._internal(
(ref) => detectStringLanguage(ref as DetectStringLanguageRef, text),
from: detectStringLanguageProvider,
name: r'detectStringLanguageProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$detectStringLanguageHash,
dependencies: DetectStringLanguageFamily._dependencies,
allTransitiveDependencies:
DetectStringLanguageFamily._allTransitiveDependencies,
text: text,
);
DetectStringLanguageProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.text,
}) : super.internal();
final String text;
@override
Override overrideWith(
String? Function(DetectStringLanguageRef provider) create,
) {
return ProviderOverride(
origin: this,
override: DetectStringLanguageProvider._internal(
(ref) => create(ref as DetectStringLanguageRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
text: text,
),
);
}
@override
AutoDisposeProviderElement<String?> createElement() {
return _DetectStringLanguageProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is DetectStringLanguageProvider && other.text == text;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, text.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin DetectStringLanguageRef on AutoDisposeProviderRef<String?> {
/// The parameter `text` of this provider.
String get text;
}
class _DetectStringLanguageProviderElement
extends AutoDisposeProviderElement<String?>
with DetectStringLanguageRef {
_DetectStringLanguageProviderElement(super.provider);
@override
String get text => (origin as DetectStringLanguageProvider).text;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
@@ -18,6 +17,8 @@ sealed class WebSocketState with _$WebSocketState {
const factory WebSocketState.connected() = _Connected; const factory WebSocketState.connected() = _Connected;
const factory WebSocketState.connecting() = _Connecting; const factory WebSocketState.connecting() = _Connecting;
const factory WebSocketState.disconnected() = _Disconnected; const factory WebSocketState.disconnected() = _Disconnected;
const factory WebSocketState.serverDown() = _ServerDown;
const factory WebSocketState.duplicateDevice() = _DuplicateDevice;
const factory WebSocketState.error(String message) = _Error; const factory WebSocketState.error(String message) = _Error;
} }
@@ -46,6 +47,10 @@ class WebSocketService {
final StreamController<WebSocketState> _statusStreamController = final StreamController<WebSocketState> _statusStreamController =
StreamController<WebSocketState>.broadcast(); StreamController<WebSocketState>.broadcast();
Timer? _reconnectTimer; Timer? _reconnectTimer;
Timer? _heartbeatTimer;
DateTime? _heartbeatAt;
Duration? heartbeatDelay;
Stream<WebSocketPacket> get dataStream => _streamController.stream; Stream<WebSocketPacket> get dataStream => _streamController.stream;
Stream<WebSocketState> get statusStream => _statusStreamController.stream; Stream<WebSocketState> get statusStream => _statusStreamController.stream;
@@ -71,15 +76,28 @@ class WebSocketService {
} }
await _channel!.ready; await _channel!.ready;
_statusStreamController.sink.add(WebSocketState.connected()); _statusStreamController.sink.add(WebSocketState.connected());
_scheduleHeartbeat();
_channel!.stream.listen( _channel!.stream.listen(
(data) { (data) {
final dataStr = final dataStr =
data is Uint8List ? utf8.decode(data) : data.toString(); data is Uint8List ? utf8.decode(data) : data.toString();
final packet = WebSocketPacket.fromJson(jsonDecode(dataStr)); final packet = WebSocketPacket.fromJson(jsonDecode(dataStr));
if (packet.type == 'error.dupe') {
_statusStreamController.sink.add(WebSocketState.duplicateDevice());
_channel!.sink.close();
return;
}
_streamController.sink.add(packet); _streamController.sink.add(packet);
log( log(
"[WebSocket] Received packet: ${packet.type} ${packet.errorMessage}", "[WebSocket] Received packet: ${packet.type} ${packet.errorMessage}",
); );
if (packet.type == 'pong' && _heartbeatAt != null) {
var now = DateTime.now();
heartbeatDelay = now.difference(_heartbeatAt!);
log(
"[WebSocket] Server respond last heartbeat for ${heartbeatDelay!.inMilliseconds} ms",
);
}
}, },
onDone: () { onDone: () {
log('[WebSocket] Connection closed, attempting to reconnect...'); log('[WebSocket] Connection closed, attempting to reconnect...');
@@ -108,6 +126,19 @@ class WebSocketService {
}); });
} }
void _scheduleHeartbeat() {
_heartbeatTimer?.cancel();
_heartbeatTimer = Timer.periodic(const Duration(seconds: 60), (_) {
_beatTheHeart();
});
}
void _beatTheHeart() {
_heartbeatAt = DateTime.now();
log('[WebSocket] We\'re beating the heart! $_heartbeatAt');
sendMessage(jsonEncode(WebSocketPacket(type: 'ping', data: null)));
}
WebSocketChannel? get ws => _channel; WebSocketChannel? get ws => _channel;
void sendMessage(String message) { void sendMessage(String message) {

View File

@@ -61,13 +61,15 @@ extension WebSocketStatePatterns on WebSocketState {
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( _Connected value)? connected,TResult Function( _Connecting value)? connecting,TResult Function( _Disconnected value)? disconnected,TResult Function( _Error value)? error,required TResult orElse(),}){ @optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( _Connected value)? connected,TResult Function( _Connecting value)? connecting,TResult Function( _Disconnected value)? disconnected,TResult Function( _ServerDown value)? serverDown,TResult Function( _DuplicateDevice value)? duplicateDevice,TResult Function( _Error value)? error,required TResult orElse(),}){
final _that = this; final _that = this;
switch (_that) { switch (_that) {
case _Connected() when connected != null: case _Connected() when connected != null:
return connected(_that);case _Connecting() when connecting != null: return connected(_that);case _Connecting() when connecting != null:
return connecting(_that);case _Disconnected() when disconnected != null: return connecting(_that);case _Disconnected() when disconnected != null:
return disconnected(_that);case _Error() when error != null: return disconnected(_that);case _ServerDown() when serverDown != null:
return serverDown(_that);case _DuplicateDevice() when duplicateDevice != null:
return duplicateDevice(_that);case _Error() when error != null:
return error(_that);case _: return error(_that);case _:
return orElse(); return orElse();
@@ -86,13 +88,15 @@ return error(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( _Connected value) connected,required TResult Function( _Connecting value) connecting,required TResult Function( _Disconnected value) disconnected,required TResult Function( _Error value) error,}){ @optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( _Connected value) connected,required TResult Function( _Connecting value) connecting,required TResult Function( _Disconnected value) disconnected,required TResult Function( _ServerDown value) serverDown,required TResult Function( _DuplicateDevice value) duplicateDevice,required TResult Function( _Error value) error,}){
final _that = this; final _that = this;
switch (_that) { switch (_that) {
case _Connected(): case _Connected():
return connected(_that);case _Connecting(): return connected(_that);case _Connecting():
return connecting(_that);case _Disconnected(): return connecting(_that);case _Disconnected():
return disconnected(_that);case _Error(): return disconnected(_that);case _ServerDown():
return serverDown(_that);case _DuplicateDevice():
return duplicateDevice(_that);case _Error():
return error(_that);} return error(_that);}
} }
/// A variant of `map` that fallback to returning `null`. /// A variant of `map` that fallback to returning `null`.
@@ -107,13 +111,15 @@ return error(_that);}
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( _Connected value)? connected,TResult? Function( _Connecting value)? connecting,TResult? Function( _Disconnected value)? disconnected,TResult? Function( _Error value)? error,}){ @optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( _Connected value)? connected,TResult? Function( _Connecting value)? connecting,TResult? Function( _Disconnected value)? disconnected,TResult? Function( _ServerDown value)? serverDown,TResult? Function( _DuplicateDevice value)? duplicateDevice,TResult? Function( _Error value)? error,}){
final _that = this; final _that = this;
switch (_that) { switch (_that) {
case _Connected() when connected != null: case _Connected() when connected != null:
return connected(_that);case _Connecting() when connecting != null: return connected(_that);case _Connecting() when connecting != null:
return connecting(_that);case _Disconnected() when disconnected != null: return connecting(_that);case _Disconnected() when disconnected != null:
return disconnected(_that);case _Error() when error != null: return disconnected(_that);case _ServerDown() when serverDown != null:
return serverDown(_that);case _DuplicateDevice() when duplicateDevice != null:
return duplicateDevice(_that);case _Error() when error != null:
return error(_that);case _: return error(_that);case _:
return null; return null;
@@ -131,12 +137,14 @@ return error(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function()? connected,TResult Function()? connecting,TResult Function()? disconnected,TResult Function( String message)? error,required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function()? connected,TResult Function()? connecting,TResult Function()? disconnected,TResult Function()? serverDown,TResult Function()? duplicateDevice,TResult Function( String message)? error,required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _Connected() when connected != null: case _Connected() when connected != null:
return connected();case _Connecting() when connecting != null: return connected();case _Connecting() when connecting != null:
return connecting();case _Disconnected() when disconnected != null: return connecting();case _Disconnected() when disconnected != null:
return disconnected();case _Error() when error != null: return disconnected();case _ServerDown() when serverDown != null:
return serverDown();case _DuplicateDevice() when duplicateDevice != null:
return duplicateDevice();case _Error() when error != null:
return error(_that.message);case _: return error(_that.message);case _:
return orElse(); return orElse();
@@ -155,12 +163,14 @@ return error(_that.message);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function() connected,required TResult Function() connecting,required TResult Function() disconnected,required TResult Function( String message) error,}) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function() connected,required TResult Function() connecting,required TResult Function() disconnected,required TResult Function() serverDown,required TResult Function() duplicateDevice,required TResult Function( String message) error,}) {final _that = this;
switch (_that) { switch (_that) {
case _Connected(): case _Connected():
return connected();case _Connecting(): return connected();case _Connecting():
return connecting();case _Disconnected(): return connecting();case _Disconnected():
return disconnected();case _Error(): return disconnected();case _ServerDown():
return serverDown();case _DuplicateDevice():
return duplicateDevice();case _Error():
return error(_that.message);} return error(_that.message);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
@@ -175,12 +185,14 @@ return error(_that.message);}
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function()? connected,TResult? Function()? connecting,TResult? Function()? disconnected,TResult? Function( String message)? error,}) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function()? connected,TResult? Function()? connecting,TResult? Function()? disconnected,TResult? Function()? serverDown,TResult? Function()? duplicateDevice,TResult? Function( String message)? error,}) {final _that = this;
switch (_that) { switch (_that) {
case _Connected() when connected != null: case _Connected() when connected != null:
return connected();case _Connecting() when connecting != null: return connected();case _Connecting() when connecting != null:
return connecting();case _Disconnected() when disconnected != null: return connecting();case _Disconnected() when disconnected != null:
return disconnected();case _Error() when error != null: return disconnected();case _ServerDown() when serverDown != null:
return serverDown();case _DuplicateDevice() when duplicateDevice != null:
return duplicateDevice();case _Error() when error != null:
return error(_that.message);case _: return error(_that.message);case _:
return null; return null;
@@ -303,6 +315,82 @@ String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
/// @nodoc
class _ServerDown with DiagnosticableTreeMixin implements WebSocketState {
const _ServerDown();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
properties
..add(DiagnosticsProperty('type', 'WebSocketState.serverDown'))
;
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ServerDown);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
return 'WebSocketState.serverDown()';
}
}
/// @nodoc
class _DuplicateDevice with DiagnosticableTreeMixin implements WebSocketState {
const _DuplicateDevice();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
properties
..add(DiagnosticsProperty('type', 'WebSocketState.duplicateDevice'))
;
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DuplicateDevice);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
return 'WebSocketState.duplicateDevice()';
}
}
/// @nodoc /// @nodoc

View File

@@ -7,6 +7,7 @@ import 'package:island/screens/developers/edit_app.dart';
import 'package:island/screens/developers/new_app.dart'; import 'package:island/screens/developers/new_app.dart';
import 'package:island/screens/developers/hub.dart'; import 'package:island/screens/developers/hub.dart';
import 'package:island/screens/discovery/articles.dart'; import 'package:island/screens/discovery/articles.dart';
import 'package:island/screens/posts/post_category_detail.dart';
import 'package:island/screens/posts/post_search.dart'; import 'package:island/screens/posts/post_search.dart';
import 'package:island/widgets/app_wrapper.dart'; import 'package:island/widgets/app_wrapper.dart';
import 'package:island/screens/tabs.dart'; import 'package:island/screens/tabs.dart';
@@ -28,9 +29,13 @@ import 'package:island/screens/creators/hub.dart';
import 'package:island/screens/creators/posts/post_manage_list.dart'; import 'package:island/screens/creators/posts/post_manage_list.dart';
import 'package:island/screens/creators/stickers/stickers.dart'; import 'package:island/screens/creators/stickers/stickers.dart';
import 'package:island/screens/creators/stickers/pack_detail.dart'; import 'package:island/screens/creators/stickers/pack_detail.dart';
import 'package:island/screens/stickers/marketplace.dart';
import 'package:island/screens/stickers/pack_detail.dart';
import 'package:island/screens/creators/poll/poll_list.dart';
import 'package:island/screens/creators/publishers.dart'; import 'package:island/screens/creators/publishers.dart';
import 'package:island/screens/creators/webfeed/webfeed_list.dart'; import 'package:island/screens/creators/webfeed/webfeed_list.dart';
import 'package:island/screens/creators/webfeed/webfeed_edit.dart'; import 'package:island/screens/creators/webfeed/webfeed_edit.dart';
import 'package:island/screens/poll/poll_editor.dart';
import 'package:island/screens/posts/compose.dart'; import 'package:island/screens/posts/compose.dart';
import 'package:island/screens/posts/post_detail.dart'; import 'package:island/screens/posts/post_detail.dart';
import 'package:island/screens/posts/pub_profile.dart'; import 'package:island/screens/posts/pub_profile.dart';
@@ -144,6 +149,37 @@ final routerProvider = Provider<GoRouter>((ref) {
return CreatorPostListScreen(pubName: name); return CreatorPostListScreen(pubName: name);
}, },
), ),
// Poll list route
GoRoute(
name: 'creatorPolls',
path: '/creators/:name/polls',
builder: (context, state) {
final name = state.pathParameters['name']!;
return CreatorPollListScreen(pubName: name);
},
),
// Poll routes
GoRoute(
name: 'creatorPollNew',
path: '/creators/:name/polls/new',
builder: (context, state) {
final name = state.pathParameters['name']!;
// initialPollId left null for create; initialPublisher prefilled
return PollEditorScreen(initialPublisher: name);
},
),
GoRoute(
name: 'creatorPollEdit',
path: '/creators/:name/polls/:id/edit',
builder: (context, state) {
final name = state.pathParameters['name']!;
final id = state.pathParameters['id']!;
return PollEditorScreen(
initialPollId: id,
initialPublisher: name,
);
},
),
GoRoute( GoRoute(
name: 'creatorStickers', name: 'creatorStickers',
path: '/creators/:name/stickers', path: '/creators/:name/stickers',
@@ -287,21 +323,6 @@ final routerProvider = Provider<GoRouter>((ref) {
builder: (context, state) => const AboutScreen(), builder: (context, state) => const AboutScreen(),
), ),
GoRoute(
name: 'reportList',
path: '/safety/reports/me',
builder: (context, state) => const AbuseReportListScreen(),
),
GoRoute(
name: 'reportDetail',
path: '/safety/reports/me/:id',
builder: (context, state) {
final id = state.pathParameters['id']!;
return AbuseReportDetailScreen(reportId: id);
},
),
// Main tabs with TabsScreen shell // Main tabs with TabsScreen shell
ShellRoute( ShellRoute(
navigatorKey: _tabsShellKey, navigatorKey: _tabsShellKey,
@@ -328,6 +349,25 @@ final routerProvider = Provider<GoRouter>((ref) {
return PostDetailScreen(id: id); return PostDetailScreen(id: id);
}, },
), ),
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( GoRoute(
name: 'publisherProfile', name: 'publisherProfile',
path: '/publishers/:name', path: '/publishers/:name',
@@ -424,6 +464,23 @@ final routerProvider = Provider<GoRouter>((ref) {
path: '/account', path: '/account',
builder: (context, state) => const AccountScreen(), builder: (context, state) => const AccountScreen(),
), ),
// Sticker marketplace (user-facing, no publisher)
GoRoute(
name: 'stickerMarketplace',
path: '/stickers',
builder:
(context, state) => const MarketplaceStickersScreen(),
routes: [
GoRoute(
name: 'stickerPackDetail',
path: ':packId',
builder: (context, state) {
final packId = state.pathParameters['packId']!;
return MarketplaceStickerPackDetailScreen(id: packId);
},
),
],
),
GoRoute( GoRoute(
name: 'notifications', name: 'notifications',
path: '/account/notifications', path: '/account/notifications',
@@ -439,14 +496,6 @@ final routerProvider = Provider<GoRouter>((ref) {
path: '/account/relationships', path: '/account/relationships',
builder: (context, state) => const RelationshipScreen(), builder: (context, state) => const RelationshipScreen(),
), ),
GoRoute(
name: 'accountProfile',
path: '/account/:name',
builder: (context, state) {
final name = state.pathParameters['name']!;
return AccountProfileScreen(name: name);
},
),
GoRoute( GoRoute(
name: 'profileUpdate', name: 'profileUpdate',
path: '/account/me/update', path: '/account/me/update',
@@ -462,8 +511,30 @@ final routerProvider = Provider<GoRouter>((ref) {
path: '/account/me/settings', path: '/account/me/settings',
builder: (context, state) => const AccountSettingsScreen(), builder: (context, state) => const AccountSettingsScreen(),
), ),
GoRoute(
name: 'reportList',
path: '/safety/reports/me',
builder: (context, state) => const AbuseReportListScreen(),
),
GoRoute(
name: 'reportDetail',
path: '/safety/reports/me/:id',
builder: (context, state) {
final id = state.pathParameters['id']!;
return AbuseReportDetailScreen(reportId: id);
},
),
], ],
), ),
GoRoute(
name: 'accountProfile',
path: '/account/:name',
builder: (context, state) {
final name = state.pathParameters['name']!;
return AccountProfileScreen(name: name);
},
),
], ],
), ),
], ],

View File

@@ -11,6 +11,8 @@ import 'package:island/widgets/app_scaffold.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:island/services/update_service.dart';
import 'package:island/widgets/content/sheet.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
@@ -93,13 +95,17 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
final theme = Theme.of(context); final theme = Theme.of(context);
return AppScaffold( return AppScaffold(
isNoBackground: false,
appBar: AppBar(title: Text('about'.tr()), elevation: 0), appBar: AppBar(title: Text('about'.tr()), elevation: 0),
body: body:
_isLoading _isLoading
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: _errorMessage != null : _errorMessage != null
? Center(child: Text(_errorMessage!)) ? Center(child: Text(_errorMessage!))
: SingleChildScrollView( : Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 540),
child: SingleChildScrollView(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
@@ -107,9 +113,8 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
// App Icon and Name // App Icon and Name
CircleAvatar( CircleAvatar(
radius: 50, radius: 50,
backgroundColor: theme.colorScheme.primary.withOpacity( backgroundColor: theme.colorScheme.primary
0.1, .withOpacity(0.1),
),
child: Image.asset( child: Image.asset(
'assets/icons/icon.png', 'assets/icons/icon.png',
width: 56, width: 56,
@@ -125,7 +130,10 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
), ),
Text( Text(
'aboutScreenVersionInfo'.tr( 'aboutScreenVersionInfo'.tr(
args: [_packageInfo.version, _packageInfo.buildNumber], args: [
_packageInfo.version,
_packageInfo.buildNumber,
],
), ),
style: theme.textTheme.bodyMedium?.copyWith( style: theme.textTheme.bodyMedium?.copyWith(
color: theme.textTheme.bodySmall?.color, color: theme.textTheme.bodySmall?.color,
@@ -189,6 +197,45 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
context, context,
title: 'aboutScreenLinksSectionTitle'.tr(), title: 'aboutScreenLinksSectionTitle'.tr(),
children: [ children: [
_buildListTile(
context,
icon: Symbols.system_update,
title: 'Check for updates',
onTap: () async {
// Fetch latest release and show the unified sheet
final svc = UpdateService();
// Reuse service fetch + compare to decide content
final release = await svc.fetchLatestRelease();
if (release != null) {
await svc.showUpdateSheet(context, release);
} else {
// Fallback: show a simple sheet indicating no info
// Use your SheetScaffold for consistent styling
// Show a minimal message
// ignore: use_build_context_synchronously
showModalBottomSheet(
context: context,
isScrollControlled: true,
useSafeArea: true,
showDragHandle: true,
backgroundColor:
Theme.of(context).colorScheme.surface,
builder:
(_) => const SheetScaffold(
titleText: 'Update',
child: Center(
child: Padding(
padding: EdgeInsets.all(24),
child: Text(
'Unable to fetch release info at this time.',
),
),
),
),
);
}
},
),
_buildListTile( _buildListTile(
context, context,
icon: Symbols.privacy_tip, icon: Symbols.privacy_tip,
@@ -204,7 +251,7 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
title: 'aboutScreenTermsOfServiceTitle'.tr(), title: 'aboutScreenTermsOfServiceTitle'.tr(),
onTap: onTap:
() => _launchURL( () => _launchURL(
'https://solsynth.dev/terms/basic-law', 'https://solsynth.dev/terms/user-agreement',
), ),
), ),
_buildListTile( _buildListTile(
@@ -235,7 +282,8 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
icon: Symbols.email, icon: Symbols.email,
title: 'aboutScreenContactUsTitle'.tr(), title: 'aboutScreenContactUsTitle'.tr(),
subtitle: 'lily@solsynth.dev', subtitle: 'lily@solsynth.dev',
onTap: () => _launchURL('mailto:lily@solsynth.dev'), onTap:
() => _launchURL('mailto:lily@solsynth.dev'),
), ),
_buildListTile( _buildListTile(
context, context,
@@ -291,6 +339,8 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
], ],
), ),
), ),
),
),
); );
} }

View File

@@ -1,12 +1,8 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:flutter/services.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/message.dart';
import 'package:island/pods/network.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/pods/userinfo.dart';
import 'package:island/screens/notification.dart'; import 'package:island/screens/notification.dart';
import 'package:island/services/responsive.dart'; import 'package:island/services/responsive.dart';
@@ -15,6 +11,7 @@ import 'package:island/widgets/account/status.dart';
import 'package:island/widgets/account/leveling_progress.dart'; import 'package:island/widgets/account/leveling_progress.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/debug_sheet.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -64,7 +61,7 @@ class AccountScreen extends HookConsumerWidget {
} }
return AppScaffold( return AppScaffold(
noBackground: isWide, isNoBackground: isWide,
appBar: AppBar(backgroundColor: Colors.transparent, toolbarHeight: 0), appBar: AppBar(backgroundColor: Colors.transparent, toolbarHeight: 0),
body: SingleChildScrollView( body: SingleChildScrollView(
padding: getTabbedPadding(context), padding: getTabbedPadding(context),
@@ -189,7 +186,6 @@ class AccountScreen extends HookConsumerWidget {
), ),
], ],
).padding(horizontal: 8), ).padding(horizontal: 8),
const Gap(8),
ListTile( ListTile(
minTileHeight: 48, minTileHeight: 48,
leading: const Icon(Symbols.notifications), leading: const Icon(Symbols.notifications),
@@ -228,10 +224,20 @@ class AccountScreen extends HookConsumerWidget {
context.pushNamed('relationships'); context.pushNamed('relationships');
}, },
), ),
ListTile(
minTileHeight: 48,
leading: const Icon(Symbols.emoji_emotions),
trailing: const Icon(Symbols.chevron_right),
contentPadding: EdgeInsets.symmetric(horizontal: 24),
title: Text('stickers').tr(),
onTap: () {
context.pushNamed('stickerMarketplace');
},
),
ListTile( ListTile(
minTileHeight: 48, minTileHeight: 48,
title: Text('abuseReports').tr(), title: Text('abuseReports').tr(),
contentPadding: const EdgeInsets.only(left: 24, right: 17), contentPadding: const EdgeInsets.symmetric(horizontal: 24),
leading: const Icon(Symbols.gavel), leading: const Icon(Symbols.gavel),
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
onTap: () => context.pushNamed('reportList'), onTap: () => context.pushNamed('reportList'),
@@ -267,30 +273,6 @@ class AccountScreen extends HookConsumerWidget {
context.pushNamed('accountSettings'); context.pushNamed('accountSettings');
}, },
), ),
if (kDebugMode) const Divider(height: 1).padding(vertical: 8),
if (kDebugMode)
ListTile(
minTileHeight: 48,
leading: const Icon(Symbols.copy_all),
trailing: const Icon(Symbols.chevron_right),
contentPadding: EdgeInsets.symmetric(horizontal: 24),
title: Text('Copy access token'),
onTap: () async {
final tk = ref.watch(tokenProvider);
Clipboard.setData(ClipboardData(text: tk!.token));
},
),
if (kDebugMode)
ListTile(
minTileHeight: 48,
leading: const Icon(Symbols.delete),
trailing: const Icon(Symbols.chevron_right),
contentPadding: EdgeInsets.symmetric(horizontal: 24),
title: Text('Reset database'),
onTap: () async {
resetDatabase(ref);
},
),
const Divider(height: 1).padding(vertical: 8), const Divider(height: 1).padding(vertical: 8),
ListTile( ListTile(
minTileHeight: 48, minTileHeight: 48,
@@ -302,6 +284,19 @@ class AccountScreen extends HookConsumerWidget {
context.pushNamed('about'); context.pushNamed('about');
}, },
), ),
ListTile(
minTileHeight: 48,
leading: const Icon(Symbols.bug_report),
trailing: const Icon(Symbols.chevron_right),
contentPadding: EdgeInsets.symmetric(horizontal: 24),
title: Text('debugOptions').tr(),
onTap: () {
showModalBottomSheet(
context: context,
builder: (context) => DebugSheet(),
);
},
),
ListTile( ListTile(
minTileHeight: 48, minTileHeight: 48,
leading: const Icon(Symbols.logout), leading: const Icon(Symbols.logout),

View File

@@ -46,7 +46,7 @@ class EventCalanderScreen extends HookConsumerWidget {
} }
return AppScaffold( return AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar( appBar: AppBar(
leading: const PageBackButton(), leading: const PageBackButton(),
title: Text('eventCalander').tr(), title: Text('eventCalander').tr(),

View File

@@ -280,7 +280,7 @@ class LevelingScreen extends HookConsumerWidget {
try { try {
showLoadingModal(context); showLoadingModal(context);
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
await client.post('/subscriptions/${membership.identifier}/cancel'); await client.post('/id/subscriptions/${membership.identifier}/cancel');
ref.invalidate(accountStellarSubscriptionProvider); ref.invalidate(accountStellarSubscriptionProvider);
ref.read(userInfoProvider.notifier).fetchUser(); ref.read(userInfoProvider.notifier).fetchUser();
if (context.mounted) { if (context.mounted) {
@@ -603,7 +603,7 @@ class LevelingScreen extends HookConsumerWidget {
try { try {
showLoadingModal(context); showLoadingModal(context);
final resp = await client.post( final resp = await client.post(
'/subscriptions', '/id/subscriptions',
data: { data: {
'identifier': tierId, 'identifier': tierId,
'payment_method': 'solian.wallet', 'payment_method': 'solian.wallet',
@@ -615,7 +615,7 @@ class LevelingScreen extends HookConsumerWidget {
final subscription = SnWalletSubscription.fromJson(resp.data); final subscription = SnWalletSubscription.fromJson(resp.data);
if (subscription.status == 1) return; if (subscription.status == 1) return;
final orderResp = await client.post( final orderResp = await client.post(
'/subscriptions/${subscription.identifier}/order', '/id/subscriptions/${subscription.identifier}/order',
); );
final order = SnWalletOrder.fromJson(orderResp.data); final order = SnWalletOrder.fromJson(orderResp.data);
@@ -633,7 +633,7 @@ class LevelingScreen extends HookConsumerWidget {
if (paidOrder != null) { if (paidOrder != null) {
await client.post( await client.post(
'/subscriptions/order/handle', '/id/subscriptions/order/handle',
data: {'order_id': paidOrder.id}, data: {'order_id': paidOrder.id},
); );

View File

@@ -7,7 +7,7 @@ part of 'leveling.dart';
// ************************************************************************** // **************************************************************************
String _$accountStellarSubscriptionHash() => String _$accountStellarSubscriptionHash() =>
r'37fb821460e3ac50b5cf777c933b6779f732daee'; r'80abcdefb3868775fd8fe3c980215713efff5948';
/// See also [accountStellarSubscription]. /// See also [accountStellarSubscription].
@ProviderFor(accountStellarSubscription) @ProviderFor(accountStellarSubscription)

View File

@@ -3,6 +3,7 @@ import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:island/models/file.dart'; import 'package:island/models/file.dart';
@@ -94,6 +95,11 @@ class UpdateProfileScreen extends HookConsumerWidget {
final usernameController = useTextEditingController(text: user.value!.name); final usernameController = useTextEditingController(text: user.value!.name);
final nicknameController = useTextEditingController(text: user.value!.nick); final nicknameController = useTextEditingController(text: user.value!.nick);
final language = useState(user.value!.language); final language = useState(user.value!.language);
final links = useState<List<Map<String, String>>>(
user.value!.profile.links.entries
.map((e) => {'key': e.key, 'value': e.value})
.toList(),
);
void updateBasicInfo() async { void updateBasicInfo() async {
if (!formKeyBasicInfo.currentState!.validate()) return; if (!formKeyBasicInfo.currentState!.validate()) return;
@@ -165,6 +171,7 @@ class UpdateProfileScreen extends HookConsumerWidget {
'location': locationController.text, 'location': locationController.text,
'time_zone': timeZoneController.text, 'time_zone': timeZoneController.text,
'birthday': birthday.value?.toUtc().toIso8601String(), 'birthday': birthday.value?.toUtc().toIso8601String(),
'links': {for (var e in links.value) e['key']!: e['value']!},
}, },
); );
final userNotifier = ref.read(userInfoProvider.notifier); final userNotifier = ref.read(userInfoProvider.notifier);
@@ -558,6 +565,69 @@ class UpdateProfileScreen extends HookConsumerWidget {
), ),
), ),
), ),
Text('links').tr().bold().fontSize(18).padding(top: 16),
Column(
spacing: 8,
children: [
for (var i = 0; i < links.value.length; i++)
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: TextFormField(
initialValue: links.value[i]['key'],
decoration: InputDecoration(
labelText: 'linkKey'.tr(),
isDense: true,
),
onChanged: (value) {
links.value[i]['key'] = value;
},
onTapOutside:
(_) =>
FocusManager.instance.primaryFocus
?.unfocus(),
),
),
const Gap(8),
Expanded(
child: TextFormField(
initialValue: links.value[i]['value'],
decoration: InputDecoration(
labelText: 'linkValue'.tr(),
isDense: true,
),
onChanged: (value) {
links.value[i]['value'] = value;
},
onTapOutside:
(_) =>
FocusManager.instance.primaryFocus
?.unfocus(),
),
),
IconButton(
icon: const Icon(Symbols.delete),
onPressed: () {
links.value = List.from(links.value)
..removeAt(i);
},
),
],
),
Align(
alignment: Alignment.centerRight,
child: FilledButton.icon(
onPressed: () {
links.value = List.from(links.value)
..add({'key': '', 'value': ''});
},
label: Text('addLink').tr(),
icon: const Icon(Symbols.add),
).padding(top: 8),
),
],
),
Align( Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: TextButton.icon( child: TextButton.icon(

View File

@@ -12,6 +12,8 @@ import 'package:island/pods/event_calendar.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/pods/userinfo.dart';
import 'package:island/services/color.dart'; import 'package:island/services/color.dart';
import 'package:island/services/responsive.dart';
import 'package:island/services/text.dart';
import 'package:island/services/time.dart'; import 'package:island/services/time.dart';
import 'package:island/services/timezone/native.dart'; import 'package:island/services/timezone/native.dart';
import 'package:island/widgets/account/account_name.dart'; import 'package:island/widgets/account/account_name.dart';
@@ -22,11 +24,14 @@ import 'package:island/widgets/account/status.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/content/markdown.dart';
import 'package:island/widgets/safety/abuse_report_helper.dart'; import 'package:island/widgets/safety/abuse_report_helper.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:palette_generator/palette_generator.dart'; import 'package:palette_generator/palette_generator.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:share_plus/share_plus.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:url_launcher/url_launcher_string.dart';
part 'profile.g.dart'; part 'profile.g.dart';
@@ -248,58 +253,12 @@ class AccountProfileScreen extends HookConsumerWidget {
final user = ref.watch(userInfoProvider); final user = ref.watch(userInfoProvider);
return account.when( Widget accountBasicInfo(SnAccount data) => Padding(
data:
(data) => AppScaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
foregroundColor: appbarColor.value,
expandedHeight: 180,
pinned: true,
leading: PageBackButton(
color: appbarColor.value,
shadows: [appbarShadow],
),
flexibleSpace: Stack(
children: [
Positioned.fill(
child:
data.profile.background?.id != null
? CloudImageWidget(
file: data.profile.background,
)
: Container(
color:
Theme.of(
context,
).appBarTheme.backgroundColor,
),
),
FlexibleSpaceBar(
title: Text(
data.nick,
style: TextStyle(
color:
appbarColor.value ??
Theme.of(context).appBarTheme.foregroundColor,
shadows: [appbarShadow],
),
),
),
],
),
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
ProfilePictureWidget( ProfilePictureWidget(file: data.profile.picture, radius: 32),
file: data.profile.picture,
radius: 32,
),
const Gap(20), const Gap(20),
Expanded( Expanded(
child: Column( child: Column(
@@ -307,54 +266,52 @@ class AccountProfileScreen extends HookConsumerWidget {
children: [ children: [
Row( Row(
children: [ children: [
AccountName( AccountName(account: data, style: TextStyle(fontSize: 20)),
account: data,
style: TextStyle(fontSize: 20),
),
const Gap(6), const Gap(6),
Text( Flexible(
child: Text(
'@${data.name}', '@${data.name}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
).fontSize(14).opacity(0.85), ).fontSize(14).opacity(0.85),
],
),
AccountStatusWidget(
uname: name,
padding: EdgeInsets.zero,
), ),
], ],
), ),
), AccountStatusWidget(uname: name, padding: EdgeInsets.zero),
], ],
), ),
), ),
IconButton(
onPressed: () {
SharePlus.instance.share(
ShareParams(
uri: Uri.parse('https://id.solian.app/@${data.name}'),
), ),
if (data.badges.isNotEmpty) );
SliverToBoxAdapter( },
child: BadgeList( icon: const Icon(Symbols.share),
badges: data.badges,
).padding(horizontal: 24, bottom: 24),
),
SliverToBoxAdapter(
child: Column(
spacing: 12,
children: [
LevelingProgressCard(
level: data.profile.level,
experience: data.profile.experience,
progress: data.profile.levelingProgress,
),
if (data.profile.verification != null)
VerificationStatusCard(
mark: data.profile.verification!,
), ),
], ],
).padding(horizontal: 20),
), ),
);
SliverToBoxAdapter( Widget accountProfileBio(SnAccount data) => Card(
child: const Divider(height: 1).padding(vertical: 24), child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('bio').tr().bold().fontSize(15).padding(bottom: 8),
if (data.profile.bio.isEmpty)
Text('descriptionNone').tr().italic()
else
MarkdownTextContent(
content: data.profile.bio,
linesMargin: EdgeInsets.zero,
), ),
SliverToBoxAdapter( ],
).padding(horizontal: 24, vertical: 20),
);
Widget accountProfileDetail(SnAccount data) => Card(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
spacing: 24, spacing: 24,
@@ -365,17 +322,6 @@ class AccountProfileScreen extends HookConsumerWidget {
spacing: 2, spacing: 2,
children: buildSubcolumn(data), children: buildSubcolumn(data),
), ),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('bio').tr().bold(),
Text(
data.profile.bio.isEmpty
? 'descriptionNone'.tr()
: data.profile.bio,
),
],
),
if (data.profile.timeZone.isNotEmpty) if (data.profile.timeZone.isNotEmpty)
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -393,9 +339,7 @@ class AccountProfileScreen extends HookConsumerWidget {
).$2.formatCustomGlobal('HH:mm'), ).$2.formatCustomGlobal('HH:mm'),
), ),
Text( Text(
getTzInfo( getTzInfo(data.profile.timeZone).$1.formatOffsetLocal(),
data.profile.timeZone,
).$1.formatOffsetLocal(),
).fontSize(11), ).fontSize(11),
Text( Text(
'UTC${getTzInfo(data.profile.timeZone).$1.formatOffset()}', 'UTC${getTzInfo(data.profile.timeZone).$1.formatOffset()}',
@@ -405,18 +349,35 @@ class AccountProfileScreen extends HookConsumerWidget {
], ],
), ),
], ],
).padding(horizontal: 24), ).padding(horizontal: 24, vertical: 16),
), );
if (user.value != null) Widget accountProfileLinks(SnAccount data) => Card(
SliverToBoxAdapter( child: Column(
child: const Divider( crossAxisAlignment: CrossAxisAlignment.start,
height: 1, children: [
).padding(top: 24, bottom: 12), Text('links').tr().bold().padding(horizontal: 24, top: 12, bottom: 4),
for (final link in data.profile.links.entries)
ListTile(
title: Text(link.key.capitalizeEachWord()),
subtitle: Text(link.value),
contentPadding: EdgeInsets.symmetric(horizontal: 24),
trailing: const Icon(Symbols.chevron_right),
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(8)),
), ),
if (user.value != null) onTap: () {
SliverToBoxAdapter( launchUrlString(link.value);
child: Row( },
),
],
),
);
Widget accountAction(SnAccount data) => Card(
child: Column(
children: [
Row(
spacing: 8, spacing: 8,
children: [ children: [
if (accountRelationship.value == null || if (accountRelationship.value == null ||
@@ -432,9 +393,7 @@ class AccountProfileScreen extends HookConsumerWidget {
foregroundColor: WidgetStatePropertyAll( foregroundColor: WidgetStatePropertyAll(
accountRelationship.value == null accountRelationship.value == null
? null ? null
: Theme.of( : Theme.of(context).colorScheme.onSecondary,
context,
).colorScheme.onSecondary,
), ),
), ),
onPressed: relationshipAction, onPressed: relationshipAction,
@@ -463,9 +422,7 @@ class AccountProfileScreen extends HookConsumerWidget {
foregroundColor: WidgetStatePropertyAll( foregroundColor: WidgetStatePropertyAll(
accountRelationship.value == null accountRelationship.value == null
? null ? null
: Theme.of( : Theme.of(context).colorScheme.onSecondary,
context,
).colorScheme.onSecondary,
), ),
), ),
onPressed: blockAction, onPressed: blockAction,
@@ -482,10 +439,8 @@ class AccountProfileScreen extends HookConsumerWidget {
), ),
), ),
], ],
).padding(horizontal: 16),
), ),
SliverToBoxAdapter( Row(
child: Row(
spacing: 8, spacing: 8,
children: [ children: [
Expanded( Expanded(
@@ -519,20 +474,213 @@ class AccountProfileScreen extends HookConsumerWidget {
), ),
), ),
], ],
).padding(horizontal: 16, top: 4), ),
],
).padding(horizontal: 16, vertical: 12),
);
return account.when(
data:
(data) => AppScaffold(
isNoBackground: false,
appBar:
isWideScreen(context)
? AppBar(
foregroundColor: appbarColor.value,
leading: PageBackButton(
color: appbarColor.value,
shadows: [appbarShadow],
),
flexibleSpace: Stack(
children: [
Positioned.fill(
child:
data.profile.background?.id != null
? CloudImageWidget(
file: data.profile.background,
)
: Container(
color:
Theme.of(
context,
).appBarTheme.backgroundColor,
),
),
FlexibleSpaceBar(
title: Text(
data.nick,
style: TextStyle(
color:
appbarColor.value ??
Theme.of(
context,
).appBarTheme.foregroundColor,
shadows: [appbarShadow],
),
),
),
],
),
)
: null,
body:
isWideScreen(context)
? Row(
children: [
Flexible(
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(child: accountBasicInfo(data)),
if (data.badges.isNotEmpty)
SliverToBoxAdapter(
child: Card(
child: BadgeList(
badges: data.badges,
).padding(horizontal: 26, vertical: 20),
).padding(left: 2, right: 4),
), ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: const Divider(height: 1).padding(top: 12), child: Column(
spacing: 12,
children: [
LevelingProgressCard(
level: data.profile.level,
experience: data.profile.experience,
progress: data.profile.levelingProgress,
).padding(left: 2, right: 4),
if (data.profile.verification != null)
Card(
margin: EdgeInsets.zero,
child: VerificationStatusCard(
mark: data.profile.verification!,
),
),
],
).padding(horizontal: 4, top: 8),
),
SliverToBoxAdapter(
child: accountProfileBio(data).padding(top: 4),
),
SliverToBoxAdapter(
child: accountProfileLinks(data),
),
SliverToBoxAdapter(
child: accountProfileDetail(data),
),
],
),
),
Flexible(
child: CustomScrollView(
slivers: [
SliverGap(24),
if (user.value != null)
SliverToBoxAdapter(child: accountAction(data)),
SliverToBoxAdapter(
child: Card(
child: FortuneGraphWidget(
events: accountEvents,
eventCalanderUser: data.name,
margin: EdgeInsets.zero,
),
),
),
],
),
),
],
).padding(horizontal: 24)
: CustomScrollView(
slivers: [
SliverAppBar(
foregroundColor: appbarColor.value,
expandedHeight: 180,
pinned: true,
leading: PageBackButton(
color: appbarColor.value,
shadows: [appbarShadow],
),
flexibleSpace: Stack(
children: [
Positioned.fill(
child:
data.profile.background?.id != null
? CloudImageWidget(
file: data.profile.background,
)
: Container(
color:
Theme.of(
context,
).appBarTheme.backgroundColor,
),
),
FlexibleSpaceBar(
title: Text(
data.nick,
style: TextStyle(
color:
appbarColor.value ??
Theme.of(
context,
).appBarTheme.foregroundColor,
shadows: [appbarShadow],
),
),
),
],
),
),
SliverToBoxAdapter(child: accountBasicInfo(data)),
if (data.badges.isNotEmpty)
SliverToBoxAdapter(
child: Card(
child: BadgeList(
badges: data.badges,
).padding(horizontal: 26, vertical: 20),
).padding(horizontal: 4),
), ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: Column( child: Column(
children: [ children: [
FortuneGraphWidget( LevelingProgressCard(
level: data.profile.level,
experience: data.profile.experience,
progress: data.profile.levelingProgress,
).padding(top: 8, horizontal: 8, bottom: 4),
if (data.profile.verification != null)
Card(
child: VerificationStatusCard(
mark: data.profile.verification!,
),
).padding(horizontal: 4),
],
),
),
SliverToBoxAdapter(
child: accountProfileBio(data).padding(horizontal: 4),
),
SliverToBoxAdapter(
child: accountProfileLinks(
data,
).padding(horizontal: 4),
),
SliverToBoxAdapter(
child: accountProfileDetail(
data,
).padding(horizontal: 4),
),
if (user.value != null)
SliverToBoxAdapter(
child: accountAction(data).padding(horizontal: 4),
),
SliverToBoxAdapter(
child: Card(
child: FortuneGraphWidget(
events: accountEvents, events: accountEvents,
eventCalanderUser: data.name, eventCalanderUser: data.name,
), ),
], ).padding(horizontal: 4),
).padding(all: 8),
), ),
], ],
), ),

View File

@@ -73,7 +73,7 @@ class CreateAccountScreen extends HookConsumerWidget {
} }
return AppScaffold( return AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar( appBar: AppBar(
leading: const PageBackButton(), leading: const PageBackButton(),
title: Text('createAccount').tr(), title: Text('createAccount').tr(),

View File

@@ -55,7 +55,7 @@ class LoginScreen extends HookConsumerWidget {
final factorPicked = useState<SnAuthFactor?>(null); final factorPicked = useState<SnAuthFactor?>(null);
return AppScaffold( return AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar( appBar: AppBar(
leading: const PageBackButton(), leading: const PageBackButton(),
title: Text('login').tr(), title: Text('login').tr(),

View File

@@ -80,7 +80,7 @@ class _OidcScreenState extends ConsumerState<OidcScreen> {
: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
), ),
initialUrlRequest: URLRequest( initialUrlRequest: URLRequest(
url: WebUri('$serverUrl/auth/login/${widget.provider}'), url: WebUri('$serverUrl/id/auth/login/${widget.provider}'),
headers: { headers: {
if (token?.token.isNotEmpty ?? false) if (token?.token.isNotEmpty ?? false)
'Authorization': 'AtField ${token!.token}', 'Authorization': 'AtField ${token!.token}',
@@ -120,7 +120,7 @@ class _OidcScreenState extends ConsumerState<OidcScreen> {
final queryParams = url.queryParameters; final queryParams = url.queryParameters;
// Check if we're on the token page // Check if we're on the token page
if (path.endsWith('/id/auth/callback')) { if (path.endsWith('/auth/callback')) {
// Extract token from URL // Extract token from URL
final challenge = queryParams['challenge']; final challenge = queryParams['challenge'];
// Return the token and close the webview // Return the token and close the webview
@@ -205,7 +205,7 @@ class _OidcScreenState extends ConsumerState<OidcScreen> {
onPressed: () { onPressed: () {
if (currentUrl != null) { if (currentUrl != null) {
Clipboard.setData(ClipboardData(text: currentUrl!)); Clipboard.setData(ClipboardData(text: currentUrl!));
showSnackBar('copyToClipboard'); showSnackBar('copyToClipboard'.tr());
} }
}, },
), ),

View File

@@ -1,14 +1,16 @@
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart' hide ConnectionState;
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/call.dart'; import 'package:island/pods/call.dart';
import 'package:island/services/responsive.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/chat/call_button.dart'; import 'package:island/widgets/chat/call_button.dart';
import 'package:island/widgets/chat/call_overlay.dart'; import 'package:island/widgets/chat/call_overlay.dart';
import 'package:island/widgets/chat/call_participant_tile.dart'; import 'package:island/widgets/chat/call_participant_tile.dart';
import 'package:island/widgets/alert.dart';
import 'package:livekit_client/livekit_client.dart'; import 'package:livekit_client/livekit_client.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -21,17 +23,39 @@ class CallScreen extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final ongoingCall = ref.watch(ongoingCallProvider(roomId)); final ongoingCall = ref.watch(ongoingCallProvider(roomId));
final callState = ref.watch(callNotifierProvider); final callState = ref.watch(callNotifierProvider);
final callNotifier = ref.read(callNotifierProvider.notifier); final callNotifier = ref.watch(callNotifierProvider.notifier);
useEffect(() { useEffect(() {
log('[Call] Joining the call...');
callNotifier.joinRoom(roomId).catchError((_) {
showConfirmAlert(
'Seems there already has a call connected, do you want override it?',
'Call already connected',
).then((value) {
if (value != true) return;
log('[Call] Joining the call... with overrides');
callNotifier.disconnect();
callNotifier.dispose();
callNotifier.joinRoom(roomId); callNotifier.joinRoom(roomId);
});
});
return null; return null;
}, []); }, []);
final viewMode = useState<String>('grid'); final allAudioOnly = callNotifier.participants.every(
(p) =>
!(p.hasVideo &&
p.remoteParticipant.trackPublications.values.any(
(pub) =>
pub.track != null &&
pub.kind == TrackType.VIDEO &&
!pub.muted &&
!pub.isDisposed,
)),
);
return AppScaffold( return AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar( appBar: AppBar(
leading: PageBackButton(), leading: PageBackButton(),
title: Column( title: Column(
@@ -44,45 +68,55 @@ class CallScreen extends HookConsumerWidget {
Text( Text(
callState.isConnected callState.isConnected
? formatDuration(callState.duration) ? formatDuration(callState.duration)
: 'Connecting', : (switch (callNotifier.room?.connectionState) {
ConnectionState.connected => 'connected',
ConnectionState.connecting => 'connecting',
ConnectionState.reconnecting => 'reconnecting',
_ => 'disconnected',
}).tr(),
style: const TextStyle(fontSize: 14), style: const TextStyle(fontSize: 14),
), ),
], ],
), ),
actions: [ actions: [
Row( if (!allAudioOnly)
mainAxisAlignment: MainAxisAlignment.end, SingleChildScrollView(
child: Row(
spacing: 4,
children: [ children: [
IconButton( for (final live in callNotifier.participants)
icon: Icon(Symbols.grid_view), SpeakingRippleAvatar(live: live, size: 30),
tooltip: 'Grid View', const Gap(8),
onPressed: () => viewMode.value = 'grid',
color:
viewMode.value == 'grid'
? Theme.of(context).colorScheme.primary
: null,
),
IconButton(
icon: Icon(Symbols.view_agenda),
tooltip: 'Stage View',
onPressed: () => viewMode.value = 'stage',
color:
viewMode.value == 'stage'
? Theme.of(context).colorScheme.primary
: null,
),
], ],
), ),
const Gap(8), ),
], ],
), ),
body: body:
callState.error != null callState.error != null
? Center( ? Center(
child: Text( child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 320),
child: Column(
children: [
const Icon(Symbols.error_outline, size: 48),
const Gap(4),
Text(
callState.error!, callState.error!,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle(color: Colors.red), style: const TextStyle(color: Color(0xFF757575)),
),
const Gap(8),
TextButton(
onPressed: () {
callNotifier.disconnect();
callNotifier.dispose();
callNotifier.joinRoom(roomId);
},
child: Text('retry').tr(),
),
],
),
), ),
) )
: Column( : Column(
@@ -100,17 +134,8 @@ class CallScreen extends HookConsumerWidget {
child: Text('No participants in call'), child: Text('No participants in call'),
); );
} }
final participants = callNotifier.participants; final participants = callNotifier.participants;
final allAudioOnly = participants.every(
(p) =>
!(p.hasVideo &&
p.remoteParticipant.trackPublications.values
.any(
(pub) =>
pub.track != null &&
pub.kind == TrackType.VIDEO,
)),
);
if (allAudioOnly) { if (allAudioOnly) {
// Audio-only: show avatars in a compact row // Audio-only: show avatars in a compact row
return Center( return Center(
@@ -123,31 +148,16 @@ class CallScreen extends HookConsumerWidget {
runSpacing: 8, runSpacing: 8,
children: [ children: [
for (final live in participants) for (final live in participants)
Padding( SpeakingRippleAvatar(
padding: const EdgeInsets.symmetric( live: live,
horizontal: 8,
),
child: SpeakingRippleAvatar(
isSpeaking: live.isSpeaking,
audioLevel:
live.remoteParticipant.audioLevel,
pictureId:
live
.participant
.profile
?.account
.profile
.picture
?.id,
size: 72, size: 72,
), ).padding(horizontal: 4),
),
], ],
), ),
), ),
); );
} }
if (viewMode.value == 'stage') {
// Stage view: show main speaker(s) large, others in row // Stage view: show main speaker(s) large, others in row
final mainSpeakers = final mainSpeakers =
participants participants
@@ -166,95 +176,13 @@ class CallScreen extends HookConsumerWidget {
if (mainSpeakers.isEmpty && participants.isNotEmpty) { if (mainSpeakers.isEmpty && participants.isNotEmpty) {
mainSpeakers.add(participants.first); mainSpeakers.add(participants.first);
} }
final others =
participants
.where((p) => !mainSpeakers.contains(p))
.toList();
return Column( return Column(
children: [
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
for (final speaker in mainSpeakers) for (final speaker in mainSpeakers)
Expanded( Expanded(
child: child: CallParticipantTile(live: speaker),
AspectRatio(
aspectRatio: 16 / 9,
child: Card(
margin: EdgeInsets.zero,
child: ClipRRect(
borderRadius:
BorderRadius.circular(8),
child: Column(
children: [
CallParticipantTile(
live: speaker,
), ),
], ],
),
),
),
).center(),
),
],
).padding(horizontal: 12),
),
if (others.isNotEmpty)
SizedBox(
height: 100,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
for (final other in others)
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8,
),
child: CallParticipantTile(
live: other,
),
),
],
),
),
],
);
}
// Default: grid view
return GridView.builder(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount:
isWidestScreen(context)
? 4
: isWiderScreen(context)
? 3
: 2,
childAspectRatio: 16 / 9,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: participants.length,
itemBuilder: (context, idx) {
final live = participants[idx];
return AspectRatio(
aspectRatio: 16 / 9,
child: Card(
margin: EdgeInsets.zero,
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Column(
children: [CallParticipantTile(live: live)],
),
),
),
).center();
},
); );
}, },
), ),

View File

@@ -21,7 +21,6 @@ import 'package:island/services/responsive.dart';
import 'package:island/widgets/account/account_picker.dart'; import 'package:island/widgets/account/account_picker.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/chat/call_overlay.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/widgets/content/sheet.dart';
import 'package:island/widgets/realms/selection_dropdown.dart'; import 'package:island/widgets/realms/selection_dropdown.dart';
@@ -346,9 +345,7 @@ class ChatListScreen extends HookConsumerWidget {
child: const Icon(Symbols.add), child: const Icon(Symbols.add),
), ),
floatingActionButtonLocation: TabbedFabLocation(context), floatingActionButtonLocation: TabbedFabLocation(context),
body: Stack( body: Column(
children: [
Column(
children: [ children: [
Consumer( Consumer(
builder: (context, ref, _) { builder: (context, ref, _) {
@@ -383,8 +380,7 @@ class ChatListScreen extends HookConsumerWidget {
selectedTab.value == 0 || selectedTab.value == 0 ||
(selectedTab.value == 1 && (selectedTab.value == 1 &&
item.type == 1) || item.type == 1) ||
(selectedTab.value == 2 && (selectedTab.value == 2 && item.type != 1),
item.type != 1),
) )
.length, .length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
@@ -413,8 +409,7 @@ class ChatListScreen extends HookConsumerWidget {
}, },
), ),
), ),
loading: loading: () => const Center(child: CircularProgressIndicator()),
() => const Center(child: CircularProgressIndicator()),
error: error:
(error, stack) => ResponseErrorWidget( (error, stack) => ResponseErrorWidget(
error: error, error: error,
@@ -426,14 +421,6 @@ class ChatListScreen extends HookConsumerWidget {
), ),
], ],
), ),
Positioned(
left: 0,
right: 0,
bottom: getTabbedPadding(context).bottom + 8,
child: const CallOverlayBar().padding(horizontal: 16, vertical: 12),
),
],
),
); );
} }
} }

View File

@@ -35,6 +35,7 @@ import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'chat.dart'; import 'chat.dart';
import 'package:island/widgets/chat/call_button.dart'; import 'package:island/widgets/chat/call_button.dart';
import 'package:island/widgets/stickers/picker.dart';
part 'room.g.dart'; part 'room.g.dart';
@@ -1066,11 +1067,19 @@ class _ChatInput extends HookConsumerWidget {
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemCount: attachments.length, itemCount: attachments.length,
itemBuilder: (context, idx) { itemBuilder: (context, idx) {
return AttachmentPreview( return SizedBox(
height: 280,
width: 280,
child: AttachmentPreview(
item: attachments[idx], item: attachments[idx],
onRequestUpload: () => onUploadAttachment(idx), onRequestUpload: () => onUploadAttachment(idx),
onDelete: () => onDeleteAttachment(idx), onDelete: () => onDeleteAttachment(idx),
onUpdate: (value) {
attachments[idx] = value;
onAttachmentsChanged(attachments);
},
onMove: (delta) => onMoveAttachment(idx, delta), onMove: (delta) => onMoveAttachment(idx, delta),
),
); );
}, },
separatorBuilder: (_, _) => const Gap(8), separatorBuilder: (_, _) => const Gap(8),
@@ -1125,6 +1134,49 @@ class _ChatInput extends HookConsumerWidget {
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 8), padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 8),
child: Row( child: Row(
children: [ children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
tooltip: 'stickers'.tr(),
icon: const Icon(Symbols.emoji_symbols),
onPressed: () {
final size = MediaQuery.of(context).size;
showStickerPickerPopover(
context,
Offset(
20,
size.height -
480 -
MediaQuery.of(context).padding.bottom,
),
onPick: (placeholder) {
// Insert placeholder at current cursor position
final text = messageController.text;
final selection = messageController.selection;
final start =
selection.start >= 0
? selection.start
: text.length;
final end =
selection.end >= 0
? selection.end
: text.length;
final newText = text.replaceRange(
start,
end,
placeholder,
);
messageController.value = TextEditingValue(
text: newText,
selection: TextSelection.collapsed(
offset: start + placeholder.length,
),
);
},
);
},
),
PopupMenuButton( PopupMenuButton(
icon: const Icon(Symbols.photo_library), icon: const Icon(Symbols.photo_library),
itemBuilder: itemBuilder:
@@ -1151,6 +1203,8 @@ class _ChatInput extends HookConsumerWidget {
), ),
], ],
), ),
],
),
Expanded( Expanded(
child: RawKeyboardListener( child: RawKeyboardListener(
focusNode: FocusNode(), focusNode: FocusNode(),

View File

@@ -41,7 +41,7 @@ class ChatDetailScreen extends HookConsumerWidget {
try { try {
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
await client.patch( await client.patch(
'/chat/$id/members/me/notify', '/sphere/chat/$id/members/me/notify',
data: {'notify_level': level}, data: {'notify_level': level},
); );
ref.invalidate(chatroomIdentityProvider(id)); ref.invalidate(chatroomIdentityProvider(id));
@@ -59,7 +59,7 @@ class ChatDetailScreen extends HookConsumerWidget {
try { try {
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
await client.patch( await client.patch(
'/chat/$id/members/me/notify', '/sphere/chat/$id/members/me/notify',
data: {'break_until': until.toUtc().toIso8601String()}, data: {'break_until': until.toUtc().toIso8601String()},
); );
ref.invalidate(chatroomProvider(id)); ref.invalidate(chatroomProvider(id));
@@ -421,10 +421,10 @@ class _ChatRoomActionMenu extends HookConsumerWidget {
showConfirmAlert( showConfirmAlert(
'deleteChatRoomHint'.tr(), 'deleteChatRoomHint'.tr(),
'deleteChatRoom'.tr(), 'deleteChatRoom'.tr(),
).then((confirm) { ).then((confirm) async {
if (confirm) { if (confirm) {
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
client.delete('/sphere/chat/$id'); await client.delete('/sphere/chat/$id');
ref.invalidate(chatroomsJoinedProvider); ref.invalidate(chatroomsJoinedProvider);
if (context.mounted) { if (context.mounted) {
context.pop(); context.pop();
@@ -454,10 +454,10 @@ class _ChatRoomActionMenu extends HookConsumerWidget {
showConfirmAlert( showConfirmAlert(
'leaveChatRoomHint'.tr(), 'leaveChatRoomHint'.tr(),
'leaveChatRoom'.tr(), 'leaveChatRoom'.tr(),
).then((confirm) { ).then((confirm) async {
if (confirm) { if (confirm) {
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
client.delete('/sphere/chat/$id/members/me'); await client.delete('/sphere/chat/$id/members/me');
ref.invalidate(chatroomsJoinedProvider); ref.invalidate(chatroomsJoinedProvider);
if (context.mounted) { if (context.mounted) {
context.pop(); context.pop();

View File

@@ -114,9 +114,9 @@ class CreatorHubShellScreen extends StatelessWidget {
isRoot: true, isRoot: true,
child: Row( child: Row(
children: [ children: [
SizedBox(width: 360, child: const CreatorHubScreen(isAside: true)), Flexible(flex: 2, child: const CreatorHubScreen(isAside: true)),
const VerticalDivider(width: 1), const VerticalDivider(width: 1),
Expanded(child: child), Flexible(flex: 3, child: child),
], ],
), ),
); );
@@ -380,6 +380,23 @@ class CreatorHubScreen extends HookConsumerWidget {
); );
}, },
), ),
ListTile(
minTileHeight: 48,
title: const Text('Polls'),
trailing: const Icon(Symbols.chevron_right),
leading: const Icon(Symbols.poll),
contentPadding: const EdgeInsets.symmetric(
horizontal: 24,
),
onTap: () {
context.pushNamed(
'creatorPolls',
pathParameters: {
'name': currentPublisher.value!.name,
},
);
},
),
ListTile( ListTile(
minTileHeight: 48, minTileHeight: 48,
title: Text('publisherMembers').tr(), title: Text('publisherMembers').tr(),

View File

@@ -0,0 +1,179 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/poll.dart';
import 'package:island/pods/network.dart';
import 'package:island/widgets/poll/poll_feedback.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
part 'poll_list.g.dart';
@riverpod
class PollListNotifier extends _$PollListNotifier
with CursorPagingNotifierMixin<SnPoll> {
static const int _pageSize = 20;
@override
Future<CursorPagingData<SnPoll>> build(String? pubName) {
// immediately load first page
return fetch(cursor: null);
}
@override
Future<CursorPagingData<SnPoll>> fetch({required String? cursor}) async {
final client = ref.read(apiClientProvider);
final offset = cursor == null ? 0 : int.parse(cursor);
// read the current family argument passed to provider
final currentPub = pubName;
final queryParams = {
'offset': offset,
'take': _pageSize,
if (currentPub != null) 'pub': currentPub,
};
final response = await client.get(
'/sphere/polls/me',
queryParameters: queryParams,
);
final total = int.parse(response.headers.value('X-Total') ?? '0');
final List<dynamic> data = response.data;
final items = data.map((json) => SnPoll.fromJson(json)).toList();
final hasMore = offset + items.length < total;
final nextCursor = hasMore ? (offset + items.length).toString() : null;
return CursorPagingData(
items: items,
hasMore: hasMore,
nextCursor: nextCursor,
);
}
}
class CreatorPollListScreen extends HookConsumerWidget {
const CreatorPollListScreen({super.key, required this.pubName});
final String pubName;
Future<void> _createPoll(BuildContext context) async {
final result = await GoRouter.of(
context,
).pushNamed('creatorPollNew', pathParameters: {'name': pubName});
if (result is SnPoll && context.mounted) {
Navigator.of(context).maybePop(result);
}
}
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('Polls')),
floatingActionButton: FloatingActionButton(
onPressed: () => _createPoll(context),
child: const Icon(Icons.add),
),
body: RefreshIndicator(
onRefresh: () => ref.refresh(pollListNotifierProvider(pubName).future),
child: CustomScrollView(
slivers: [
PagingHelperSliverView(
provider: pollListNotifierProvider(pubName),
futureRefreshable: pollListNotifierProvider(pubName).future,
notifierRefreshable: pollListNotifierProvider(pubName).notifier,
contentBuilder:
(data, widgetCount, endItemView) => SliverList.builder(
itemCount: widgetCount,
itemBuilder: (context, index) {
if (index == widgetCount - 1) {
return endItemView;
}
final poll = data.items[index];
return _CreatorPollItem(poll: poll, pubName: pubName);
},
),
),
],
),
),
);
}
}
class _CreatorPollItem extends StatelessWidget {
final String pubName;
const _CreatorPollItem({required this.poll, required this.pubName});
final SnPoll poll;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final ended = poll.endedAt;
final endedText =
ended == null
? 'No end'
: MaterialLocalizations.of(context).formatFullDate(ended);
return Card(
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
clipBehavior: Clip.antiAlias,
child: ListTile(
title: Text(poll.title ?? 'Untitled poll'),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (poll.description != null && poll.description!.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 4),
child: Text(
poll.description!,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
Padding(
padding: const EdgeInsets.only(top: 4),
child: Text(
'Questions: ${poll.questions.length} · Ends: $endedText',
style: theme.textTheme.bodySmall,
),
),
],
),
trailing: PopupMenuButton<String>(
itemBuilder:
(context) => [
PopupMenuItem(
child: Row(
children: [
const Icon(Symbols.edit),
const Gap(16),
Text('Edit'),
],
),
onTap: () {
GoRouter.of(context).pushNamed(
'creatorPollEdit',
pathParameters: {'name': pubName, 'id': poll.id},
);
},
),
],
),
onTap: () {
showModalBottomSheet(
context: context,
useRootNavigator: true,
isScrollControlled: true,
builder:
(context) => PollFeedbackSheet(pollId: poll.id, poll: poll),
);
},
),
);
}
}

View File

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

View File

@@ -26,7 +26,7 @@ part 'pack_detail.freezed.dart';
@riverpod @riverpod
Future<List<SnSticker>> stickerPackContent(Ref ref, String packId) async { Future<List<SnSticker>> stickerPackContent(Ref ref, String packId) async {
final apiClient = ref.watch(apiClientProvider); final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get('/stickers/$packId/content'); final resp = await apiClient.get('/sphere/stickers/$packId/content');
return resp.data return resp.data
.map<SnSticker>((e) => SnSticker.fromJson(e)) .map<SnSticker>((e) => SnSticker.fromJson(e))
.cast<SnSticker>() .cast<SnSticker>()
@@ -74,9 +74,12 @@ class StickerPackDetailScreen extends HookConsumerWidget {
IconButton( IconButton(
icon: const Icon(Symbols.add_circle), icon: const Icon(Symbols.add_circle),
onPressed: () { onPressed: () {
context.pushNamed('creatorStickerNew', pathParameters: {'packId': id}).then(( context
value, .pushNamed(
) { 'creatorStickerNew',
pathParameters: {'name': pubName, 'packId': id},
)
.then((value) {
if (value != null) { if (value != null) {
ref.invalidate(stickerPackContentProvider(id)); ref.invalidate(stickerPackContentProvider(id));
} }
@@ -173,9 +176,13 @@ class StickerPackDetailScreen extends HookConsumerWidget {
title: 'edit'.tr(), title: 'edit'.tr(),
image: MenuImage.icon(Symbols.edit), image: MenuImage.icon(Symbols.edit),
callback: () { callback: () {
context.pushNamed( context
.pushNamed(
'creatorStickerEdit', 'creatorStickerEdit',
pathParameters: {'packId': id, 'id': sticker.id}, pathParameters: {
'packId': id,
'id': sticker.id,
},
) )
.then((value) { .then((value) {
if (value != null) { if (value != null) {
@@ -259,9 +266,7 @@ class _StickerPackActionMenu extends HookConsumerWidget {
(context) => [ (context) => [
PopupMenuItem( PopupMenuItem(
onTap: () { onTap: () {
context.push( context.push('/creators/$pubName/stickers/$packId/edit');
'/creators/$pubName/stickers/$packId/edit',
);
}, },
child: Row( child: Row(
children: [ children: [

View File

@@ -7,7 +7,7 @@ part of 'pack_detail.dart';
// ************************************************************************** // **************************************************************************
String _$stickerPackContentHash() => String _$stickerPackContentHash() =>
r'78de848fba1f341f217f8ae4b9eef2d8afa67964'; r'42d74f51022e67e35cb601c2f30f4f02e1f2be9d';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {

View File

@@ -31,7 +31,7 @@ class StickersScreen extends HookConsumerWidget {
context context
.pushNamed( .pushNamed(
'creatorStickerPackNew', 'creatorStickerPackNew',
queryParameters: {'pubName': pubName}, queryParameters: {'name': pubName},
) )
.then((value) { .then((value) {
if (value != null) { if (value != null) {
@@ -76,7 +76,7 @@ class SliverStickerPacksList extends HookConsumerWidget {
onTap: () { onTap: () {
context.pushNamed( context.pushNamed(
'creatorStickerPackDetail', 'creatorStickerPackDetail',
pathParameters: {'pubName': pubName, 'packId': sticker.id}, pathParameters: {'name': pubName, 'packId': sticker.id},
); );
}, },
); );

View File

@@ -18,7 +18,7 @@ part 'apps.g.dart';
@riverpod @riverpod
Future<List<CustomApp>> customApps(Ref ref, String publisherName) async { Future<List<CustomApp>> customApps(Ref ref, String publisherName) async {
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
final resp = await client.get('/developers/$publisherName/apps'); final resp = await client.get('/develop/developers/$publisherName/apps');
return resp.data.map((e) => CustomApp.fromJson(e)).cast<CustomApp>().toList(); return resp.data.map((e) => CustomApp.fromJson(e)).cast<CustomApp>().toList();
} }
@@ -37,7 +37,10 @@ class CustomAppsScreen extends HookConsumerWidget {
IconButton( IconButton(
icon: const Icon(Symbols.add), icon: const Icon(Symbols.add),
onPressed: () { onPressed: () {
context.pushNamed('developerAppNew', pathParameters: {'name': publisherName}); context.pushNamed(
'developerAppNew',
pathParameters: {'name': publisherName},
);
}, },
), ),
], ],
@@ -121,7 +124,13 @@ class CustomAppsScreen extends HookConsumerWidget {
], ],
onSelected: (value) { onSelected: (value) {
if (value == 'edit') { if (value == 'edit') {
context.pushNamed('developerAppEdit', pathParameters: {'name': publisherName, 'id': app.id}); context.pushNamed(
'developerAppEdit',
pathParameters: {
'name': publisherName,
'id': app.id,
},
);
} else if (value == 'delete') { } else if (value == 'delete') {
showConfirmAlert( showConfirmAlert(
'deleteCustomAppHint'.tr(), 'deleteCustomAppHint'.tr(),
@@ -130,7 +139,7 @@ class CustomAppsScreen extends HookConsumerWidget {
if (confirm) { if (confirm) {
final client = ref.read(apiClientProvider); final client = ref.read(apiClientProvider);
client.delete( client.delete(
'/developers/$publisherName/apps/${app.id}', '/develop/developers/$publisherName/apps/${app.id}',
); );
ref.invalidate( ref.invalidate(
customAppsProvider(publisherName), customAppsProvider(publisherName),

View File

@@ -6,7 +6,7 @@ part of 'apps.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$customAppsHash() => r'1dec11573b9d987c3adbdf4732b3781a6f40172a'; String _$customAppsHash() => r'c6ac78060eb51a2b208a749a81ecbe0a9c608ce1';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {

View File

@@ -24,7 +24,7 @@ part 'edit_app.g.dart';
@riverpod @riverpod
Future<CustomApp?> customApp(Ref ref, String publisherName, String id) async { Future<CustomApp?> customApp(Ref ref, String publisherName, String id) async {
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
final resp = await client.get('/developers/$publisherName/apps/$id'); final resp = await client.get('/develop/developers/$publisherName/apps/$id');
return CustomApp.fromJson(resp.data); return CustomApp.fromJson(resp.data);
} }
@@ -282,9 +282,15 @@ class EditAppScreen extends HookConsumerWidget {
: null, : null,
}; };
if (isNew) { if (isNew) {
await client.post('/developers/$publisherName/apps', data: data); await client.post(
'/develop/developers/$publisherName/apps',
data: data,
);
} else { } else {
await client.patch('/developers/$publisherName/apps/$id', data: data); await client.patch(
'/develop/developers/$publisherName/apps/$id',
data: data,
);
} }
ref.invalidate(customAppsProvider(publisherName)); ref.invalidate(customAppsProvider(publisherName));
if (context.mounted) { if (context.mounted) {

View File

@@ -6,7 +6,7 @@ part of 'edit_app.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$customAppHash() => r'aa4d1fb803c47a99cbacf6d91481f4fce3fda457'; String _$customAppHash() => r'42ad937b8439c793e3c5c35568bb5fa4da017df3';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {

View File

@@ -25,17 +25,17 @@ part 'hub.g.dart';
Future<DeveloperStats?> developerStats(Ref ref, String? uname) async { Future<DeveloperStats?> developerStats(Ref ref, String? uname) async {
if (uname == null) return null; if (uname == null) return null;
final apiClient = ref.watch(apiClientProvider); final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get('/sphere/developers/$uname/stats'); final resp = await apiClient.get('/develop/developers/$uname/stats');
return DeveloperStats.fromJson(resp.data); return DeveloperStats.fromJson(resp.data);
} }
@riverpod @riverpod
Future<List<SnPublisher>> developers(Ref ref) async { Future<List<SnDeveloper>> developers(Ref ref) async {
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
final resp = await client.get('/sphere/developers'); final resp = await client.get('/develop/developers');
return resp.data return resp.data
.map((e) => SnPublisher.fromJson(e)) .map((e) => SnDeveloper.fromJson(e))
.cast<SnPublisher>() .cast<SnDeveloper>()
.toList(); .toList();
} }
@@ -51,12 +51,9 @@ class DeveloperHubShellScreen extends StatelessWidget {
isRoot: true, isRoot: true,
child: Row( child: Row(
children: [ children: [
SizedBox( Flexible(flex: 2, child: const DeveloperHubScreen(isAside: true)),
width: 360,
child: const DeveloperHubScreen(isAside: true),
),
const VerticalDivider(width: 1), const VerticalDivider(width: 1),
Expanded(child: child), Flexible(flex: 3, child: child),
], ],
), ),
); );
@@ -77,25 +74,25 @@ class DeveloperHubScreen extends HookConsumerWidget {
} }
final developers = ref.watch(developersProvider); final developers = ref.watch(developersProvider);
final currentDeveloper = useState<SnPublisher?>( final currentDeveloper = useState<SnDeveloper?>(
developers.value?.firstOrNull, developers.value?.firstOrNull,
); );
final List<DropdownMenuItem<SnPublisher>> developersMenu = developers.when( final List<DropdownMenuItem<SnDeveloper>> developersMenu = developers.when(
data: data:
(data) => (data) =>
data data
.map( .map(
(item) => DropdownMenuItem<SnPublisher>( (item) => DropdownMenuItem<SnDeveloper>(
value: item, value: item,
child: ListTile( child: ListTile(
minTileHeight: 48, minTileHeight: 48,
leading: ProfilePictureWidget( leading: ProfilePictureWidget(
radius: 16, radius: 16,
fileId: item.picture?.id, fileId: item.publisher?.picture?.id,
), ),
title: Text(item.nick), title: Text(item.publisher!.nick),
subtitle: Text('@${item.name}'), subtitle: Text('@${item.publisher!.name}'),
trailing: trailing:
currentDeveloper.value?.id == item.id currentDeveloper.value?.id == item.id
? const Icon(Icons.check) ? const Icon(Icons.check)
@@ -110,17 +107,17 @@ class DeveloperHubScreen extends HookConsumerWidget {
); );
final developerStats = ref.watch( final developerStats = ref.watch(
developerStatsProvider(currentDeveloper.value?.name), developerStatsProvider(currentDeveloper.value?.publisher?.name),
); );
return AppScaffold( return AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar( appBar: AppBar(
leading: !isWide ? const PageBackButton() : null, leading: !isWide ? const PageBackButton() : null,
title: Text('developerHub').tr(), title: Text('developerHub').tr(),
actions: [ actions: [
DropdownButtonHideUnderline( DropdownButtonHideUnderline(
child: DropdownButton2<SnPublisher>( child: DropdownButton2<SnDeveloper>(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
value: currentDeveloper.value, value: currentDeveloper.value,
hint: CircleAvatar( hint: CircleAvatar(
@@ -142,7 +139,7 @@ class DeveloperHubScreen extends HookConsumerWidget {
...developersMenu.map( ...developersMenu.map(
(e) => ProfilePictureWidget( (e) => ProfilePictureWidget(
radius: 16, radius: 16,
fileId: e.value?.picture?.id, fileId: e.value?.publisher?.picture?.id,
).center().padding(right: 8), ).center().padding(right: 8),
), ),
]; ];
@@ -196,10 +193,12 @@ class DeveloperHubScreen extends HookConsumerWidget {
...(developers.value?.map( ...(developers.value?.map(
(developer) => ListTile( (developer) => ListTile(
leading: ProfilePictureWidget( leading: ProfilePictureWidget(
file: developer.picture, file: developer.publisher?.picture,
),
title: Text(developer.publisher!.nick),
subtitle: Text(
'@${developer.publisher!.name}',
), ),
title: Text(developer.nick),
subtitle: Text('@${developer.name}'),
onTap: () { onTap: () {
currentDeveloper.value = developer; currentDeveloper.value = developer;
}, },
@@ -246,7 +245,8 @@ class DeveloperHubScreen extends HookConsumerWidget {
context.pushNamed( context.pushNamed(
'developerApps', 'developerApps',
pathParameters: { pathParameters: {
'name': currentDeveloper.value!.name, 'name':
currentDeveloper.value!.publisher!.name,
}, },
); );
}, },
@@ -260,7 +260,9 @@ class DeveloperHubScreen extends HookConsumerWidget {
error: err, error: err,
onRetry: () { onRetry: () {
ref.invalidate( ref.invalidate(
developerStatsProvider(currentDeveloper.value?.name), developerStatsProvider(
currentDeveloper.value?.publisher!.name,
),
); );
}, },
), ),
@@ -339,7 +341,7 @@ class _DeveloperEnrollmentSheet extends HookConsumerWidget {
Future<void> enroll(SnPublisher publisher) async { Future<void> enroll(SnPublisher publisher) async {
try { try {
final client = ref.read(apiClientProvider); final client = ref.read(apiClientProvider);
await client.post('/sphere/developers/${publisher.name}/enroll'); await client.post('/develop/developers/${publisher.name}/enroll');
if (context.mounted) { if (context.mounted) {
Navigator.pop(context, true); Navigator.pop(context, true);
} }
@@ -357,7 +359,7 @@ class _DeveloperEnrollmentSheet extends HookConsumerWidget {
? Center( ? Center(
child: child:
Text( Text(
'noPublishersToEnroll', 'noDevelopersToEnroll',
textAlign: TextAlign.center, textAlign: TextAlign.center,
).tr(), ).tr(),
) )

View File

@@ -6,7 +6,7 @@ part of 'hub.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$developerStatsHash() => r'baa708f3586e8987e221cc8ab825d759658c0f55'; String _$developerStatsHash() => r'45546f29ec7cd1a9c3a4e0f4e39275e78bf34755';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {
@@ -149,12 +149,12 @@ class _DeveloperStatsProviderElement
String? get uname => (origin as DeveloperStatsProvider).uname; String? get uname => (origin as DeveloperStatsProvider).uname;
} }
String _$developersHash() => r'f11335fdf553c661110281edeec70ef89c64727d'; String _$developersHash() => r'252341098617ac398ce133994453f318dd3edbd2';
/// See also [developers]. /// See also [developers].
@ProviderFor(developers) @ProviderFor(developers)
final developersProvider = final developersProvider =
AutoDisposeFutureProvider<List<SnPublisher>>.internal( AutoDisposeFutureProvider<List<SnDeveloper>>.internal(
developers, developers,
name: r'developersProvider', name: r'developersProvider',
debugGetCreateSourceHash: debugGetCreateSourceHash:
@@ -167,6 +167,6 @@ final developersProvider =
@Deprecated('Will be removed in 3.0. Use Ref instead') @Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element // ignore: unused_element
typedef DevelopersRef = AutoDisposeFutureProviderRef<List<SnPublisher>>; typedef DevelopersRef = AutoDisposeFutureProviderRef<List<SnDeveloper>>;
// ignore_for_file: type=lint // ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -17,7 +17,7 @@ class DiscoveryRealmsScreen extends HookConsumerWidget {
final currentQuery = useState<String?>(null); final currentQuery = useState<String?>(null);
return AppScaffold( return AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar(title: Text('discoverRealms'.tr())), appBar: AppBar(title: Text('discoverRealms'.tr())),
body: Stack( body: Stack(
children: [ children: [

View File

@@ -12,11 +12,11 @@ import 'package:island/models/webfeed.dart';
import 'package:island/pods/event_calendar.dart'; import 'package:island/pods/event_calendar.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/pods/userinfo.dart';
import 'package:island/services/responsive.dart'; import 'package:island/services/responsive.dart';
import 'package:island/widgets/account/event_calendar.dart';
import 'package:island/widgets/account/fortune_graph.dart'; import 'package:island/widgets/account/fortune_graph.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/models/post.dart'; import 'package:island/models/post.dart';
import 'package:island/widgets/check_in.dart'; import 'package:island/widgets/check_in.dart';
import 'package:island/widgets/post/post_featured.dart';
import 'package:island/widgets/post/post_item.dart'; import 'package:island/widgets/post/post_item.dart';
import 'package:island/screens/tabs.dart'; import 'package:island/screens/tabs.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
@@ -70,22 +70,15 @@ class ExploreScreen extends HookConsumerWidget {
final events = ref.watch(eventCalendarProvider(query.value)); final events = ref.watch(eventCalendarProvider(query.value));
final selectedDay = useState(now); final selectedDay = useState(now);
void onMonthChanged(int year, int month) {
query.value = EventCalendarQuery(
uname: query.value.uname,
year: year,
month: month,
);
}
// Function to handle day selection for synchronizing between widgets // Function to handle day selection for synchronizing between widgets
void onDaySelected(DateTime day) { void onDaySelected(DateTime day) {
selectedDay.value = day; selectedDay.value = day;
} }
final user = ref.watch(userInfoProvider);
return AppScaffold( return AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar( appBar: AppBar(
toolbarHeight: 0, toolbarHeight: 0,
bottom: PreferredSize( bottom: PreferredSize(
@@ -167,7 +160,17 @@ class ExploreScreen extends HookConsumerWidget {
), ),
), ),
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: InkWell(
onLongPress: () {
context.pushNamed('postCompose', queryParameters: {'type': '1'}).then(
(value) {
if (value != null) {
activitiesNotifier.forceRefresh();
}
},
);
},
child: FloatingActionButton(
heroTag: Key("explore-page-fab"), heroTag: Key("explore-page-fab"),
onPressed: () { onPressed: () {
context.pushNamed('postCompose').then((value) { context.pushNamed('postCompose').then((value) {
@@ -178,48 +181,47 @@ class ExploreScreen extends HookConsumerWidget {
}, },
child: const Icon(Symbols.edit), child: const Icon(Symbols.edit),
), ),
),
floatingActionButtonLocation: TabbedFabLocation(context), floatingActionButtonLocation: TabbedFabLocation(context),
body: Builder( body: Builder(
builder: (context) { builder: (context) {
final isWider = isWiderScreen(context); final isWider = isWiderScreen(context);
final bodyView = TabBarView( final bodyView = _buildActivityList(
controller: tabController, context,
physics: const NeverScrollableScrollPhysics(), ref,
children: [ currentFilter.value,
_buildActivityList(context, ref, null),
_buildActivityList(context, ref, 'subscriptions'),
_buildActivityList(context, ref, 'friends'),
],
); );
if (isWider) { if (isWider) {
return Row( return Row(
children: [ children: [
Flexible(flex: 3, child: bodyView), Flexible(flex: 3, child: bodyView.padding(left: 8)),
const VerticalDivider(width: 1), if (user.value != null)
Flexible( Flexible(
flex: 2, flex: 2,
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
children: [ children: [
CheckInWidget(), CheckInWidget(
Card( margin: EdgeInsets.only(
margin: EdgeInsets.only(left: 16, right: 16, top: 8), left: 8,
child: Column( right: 12,
children: [ top: 16,
// Use the reusable EventCalendarWidget
EventCalendarWidget(
events: events,
initialDate: now,
showEventDetails: true,
onMonthChanged: onMonthChanged,
onDaySelected: onDaySelected,
), ),
], onChecked: () {
ref.invalidate(
eventCalendarProvider(query.value),
);
},
), ),
PostFeaturedList().padding(
left: 8,
right: 12,
top: 8,
), ),
FortuneGraphWidget( FortuneGraphWidget(
margin: EdgeInsets.only(left: 8, right: 12, top: 8),
events: events, events: events,
constrainWidth: true, constrainWidth: true,
onPointSelected: onDaySelected, onPointSelected: onDaySelected,
@@ -227,6 +229,25 @@ class ExploreScreen extends HookConsumerWidget {
], ],
), ),
), ),
)
else
Flexible(
flex: 2,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Welcome to\nthe Solar Network',
style: Theme.of(context).textTheme.titleLarge,
).bold(),
const Gap(2),
Text(
'Login to explore more!',
style: Theme.of(context).textTheme.bodyLarge,
),
],
).padding(horizontal: 36, vertical: 16),
), ),
], ],
); );
@@ -280,7 +301,9 @@ class _DiscoveryActivityItem extends StatelessWidget {
final items = data['items'] as List; final items = data['items'] as List;
final type = items.firstOrNull?['type'] ?? 'unknown'; final type = items.firstOrNull?['type'] ?? 'unknown';
return Column( return Card(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
@@ -301,35 +324,39 @@ class _DiscoveryActivityItem extends StatelessWidget {
).padding(horizontal: 20, top: 8, bottom: 4), ).padding(horizontal: 20, top: 8, bottom: 4),
SizedBox( SizedBox(
height: 180, height: 180,
child: ListView.builder( child: ConstrainedBox(
scrollDirection: Axis.horizontal, constraints: const BoxConstraints(maxHeight: 200),
itemCount: items.length, child: CarouselView.weighted(
padding: const EdgeInsets.symmetric(horizontal: 8), flexWeights:
itemBuilder: (context, index) { isWideScreen(context) ? <int>[3, 2, 1] : <int>[4, 1],
final item = items[index]; consumeMaxWeight: false,
enableSplash: false,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8)),
),
children: [
for (final item in items)
switch (type) { switch (type) {
case 'realm': 'realm' => RealmCard(
return RealmCard(
realm: SnRealm.fromJson(item['data']), realm: SnRealm.fromJson(item['data']),
maxWidth: 280, maxWidth: 280,
); ),
case 'publisher': 'publisher' => PublisherCard(
return PublisherCard(
publisher: SnPublisher.fromJson(item['data']), publisher: SnPublisher.fromJson(item['data']),
maxWidth: 280, maxWidth: 280,
); ),
case 'article': 'article' => WebArticleCard(
return WebArticleCard(
article: SnWebArticle.fromJson(item['data']), article: SnWebArticle.fromJson(item['data']),
maxWidth: 280, maxWidth: 280,
);
default:
return Placeholder();
}
},
), ),
).padding(bottom: 4), _ => Placeholder(),
},
], ],
),
),
).padding(bottom: 8, horizontal: 8),
],
),
); );
} }
} }
@@ -355,8 +382,17 @@ class _ActivityListView extends HookConsumerWidget {
return CustomScrollView( return CustomScrollView(
slivers: [ slivers: [
SliverGap(12),
if (user.value != null && !contentOnly) if (user.value != null && !contentOnly)
SliverToBoxAdapter(child: CheckInWidget()), SliverToBoxAdapter(
child: CheckInWidget(
margin: EdgeInsets.only(left: 8, right: 8, bottom: 4),
),
),
if (!contentOnly)
SliverToBoxAdapter(
child: PostFeaturedList().padding(horizontal: 8, bottom: 4, top: 4),
),
SliverList.builder( SliverList.builder(
itemCount: widgetCount, itemCount: widgetCount,
itemBuilder: (context, index) { itemBuilder: (context, index) {
@@ -373,19 +409,9 @@ class _ActivityListView extends HookConsumerWidget {
switch (item.type) { switch (item.type) {
case 'posts.new': case 'posts.new':
case 'posts.new.replies': case 'posts.new.replies':
final isReply = item.type == 'posts.new.replies'; itemWidget = PostActionableItem(
itemWidget = PostItem( borderRadius: 8,
backgroundColor:
isWideScreen(context) ? Colors.transparent : null,
item: SnPost.fromJson(item.data!), item: SnPost.fromJson(item.data!),
padding:
isReply
? const EdgeInsets.only(
left: 16,
right: 16,
bottom: 16,
)
: null,
onRefresh: () { onRefresh: () {
activitiesNotifier.forceRefresh(); activitiesNotifier.forceRefresh();
}, },
@@ -396,21 +422,10 @@ class _ActivityListView extends HookConsumerWidget {
); );
}, },
); );
if (isReply) { itemWidget = Card(
itemWidget = Column( margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
crossAxisAlignment: CrossAxisAlignment.stretch, child: itemWidget,
children: [
Row(
children: [
const Icon(Symbols.reply),
const Gap(8),
Text('Replying your post'),
],
).padding(horizontal: 20, vertical: 8),
itemWidget,
],
); );
}
break; break;
case 'discovery': case 'discovery':
itemWidget = _DiscoveryActivityItem(data: item.data!); itemWidget = _DiscoveryActivityItem(data: item.data!);
@@ -419,7 +434,7 @@ class _ActivityListView extends HookConsumerWidget {
itemWidget = const Placeholder(); itemWidget = const Placeholder();
} }
return Column(children: [itemWidget, const Divider(height: 1)]); return itemWidget;
}, },
), ),
SliverGap(getTabbedPadding(context).bottom), SliverGap(getTabbedPadding(context).bottom),

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,8 @@ import 'package:island/widgets/post/publishers_modal.dart';
import 'package:island/screens/posts/post_detail.dart'; import 'package:island/screens/posts/post_detail.dart';
import 'package:island/widgets/post/compose_settings_sheet.dart'; import 'package:island/widgets/post/compose_settings_sheet.dart';
import 'package:island/services/compose_storage_db.dart'; import 'package:island/services/compose_storage_db.dart';
import 'package:island/widgets/post/draft_manager.dart'; // DraftManagerSheet is now imported through compose_toolbar.dart
import 'package:island/widgets/post/compose_toolbar.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -52,13 +53,13 @@ class PostEditScreen extends HookConsumerWidget {
data: (post) => PostComposeScreen(originalPost: post), data: (post) => PostComposeScreen(originalPost: post),
loading: loading:
() => AppScaffold( () => AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar(leading: const PageBackButton()), appBar: AppBar(leading: const PageBackButton()),
body: const Center(child: CircularProgressIndicator()), body: const Center(child: CircularProgressIndicator()),
), ),
error: error:
(e, _) => AppScaffold( (e, _) => AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar(leading: const PageBackButton()), appBar: AppBar(leading: const PageBackButton()),
body: Text('Error: $e', textAlign: TextAlign.center), body: Text('Error: $e', textAlign: TextAlign.center),
), ),
@@ -92,7 +93,6 @@ class PostComposeScreen extends HookConsumerWidget {
// Otherwise, continue with regular post compose // Otherwise, continue with regular post compose
final theme = Theme.of(context); final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
// When editing, preserve the original replied/forwarded post references // When editing, preserve the original replied/forwarded post references
final effectiveRepliedPost = repliedPost ?? originalPost?.repliedPost; final effectiveRepliedPost = repliedPost ?? originalPost?.repliedPost;
@@ -205,17 +205,7 @@ class PostComposeScreen extends HookConsumerWidget {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
builder: builder: (context) => ComposeSettingsSheet(state: state),
(context) => ComposeSettingsSheet(
titleController: state.titleController,
descriptionController: state.descriptionController,
visibility: state.visibility,
tagsController: state.tagsController,
categoriesController: state.categoriesController,
onVisibilityChanged: () {
// Trigger rebuild if needed
},
),
); );
} }
@@ -238,6 +228,8 @@ class PostComposeScreen extends HookConsumerWidget {
onRequestUpload: onRequestUpload:
() => ComposeLogic.uploadAttachment(ref, state, idx), () => ComposeLogic.uploadAttachment(ref, state, idx),
onDelete: () => ComposeLogic.deleteAttachment(ref, state, idx), onDelete: () => ComposeLogic.deleteAttachment(ref, state, idx),
onUpdate:
(value) => ComposeLogic.updateAttachment(state, value, idx),
onMove: (delta) { onMove: (delta) {
state.attachments.value = ComposeLogic.moveAttachment( state.attachments.value = ComposeLogic.moveAttachment(
state.attachments.value, state.attachments.value,
@@ -265,6 +257,9 @@ class PostComposeScreen extends HookConsumerWidget {
() => ComposeLogic.uploadAttachment(ref, state, idx), () => ComposeLogic.uploadAttachment(ref, state, idx),
onDelete: onDelete:
() => ComposeLogic.deleteAttachment(ref, state, idx), () => ComposeLogic.deleteAttachment(ref, state, idx),
onUpdate:
(value) =>
ComposeLogic.updateAttachment(state, value, idx),
onMove: (delta) { onMove: (delta) {
state.attachments.value = ComposeLogic.moveAttachment( state.attachments.value = ComposeLogic.moveAttachment(
state.attachments.value, state.attachments.value,
@@ -287,43 +282,10 @@ class PostComposeScreen extends HookConsumerWidget {
} }
}, },
child: AppScaffold( child: AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar( appBar: AppBar(
leading: const PageBackButton(), leading: const PageBackButton(),
actions: [ actions: [
if (originalPost == null) // Only show drafts for new posts
IconButton(
icon: const Icon(Symbols.draft),
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder:
(context) => DraftManagerSheet(
onDraftSelected: (draftId) {
final draft =
ref.read(
composeStorageNotifierProvider,
)[draftId];
if (draft != null) {
state.titleController.text = draft.title ?? '';
state.descriptionController.text =
draft.description ?? '';
state.contentController.text =
draft.content ?? '';
state.visibility.value = draft.visibility;
}
},
),
);
},
tooltip: 'drafts'.tr(),
),
IconButton(
icon: const Icon(Symbols.save),
onPressed: () => ComposeLogic.saveDraft(ref, state),
tooltip: 'saveDraft'.tr(),
),
IconButton( IconButton(
icon: const Icon(Symbols.settings), icon: const Icon(Symbols.settings),
onPressed: showSettingsSheet, onPressed: showSettingsSheet,
@@ -397,15 +359,9 @@ class PostComposeScreen extends HookConsumerWidget {
// Post content form // Post content form
Expanded( Expanded(
child: SingleChildScrollView( child: KeyboardListener(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Content field with borderless design
RawKeyboardListener(
focusNode: FocusNode(), focusNode: FocusNode(),
onKey: onKeyEvent:
(event) => ComposeLogic.handleKeyPress( (event) => ComposeLogic.handleKeyPress(
event, event,
state, state,
@@ -415,13 +371,61 @@ class PostComposeScreen extends HookConsumerWidget {
repliedPost: repliedPost, repliedPost: repliedPost,
forwardedPost: forwardedPost, forwardedPost: forwardedPost,
), ),
child: TextField( child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: state.titleController,
decoration: InputDecoration(
hintText: 'postTitle'.tr(),
border: InputBorder.none,
isCollapsed: true,
contentPadding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 8,
),
),
style: theme.textTheme.titleMedium,
onTapOutside:
(_) =>
FocusManager.instance.primaryFocus
?.unfocus(),
),
TextField(
controller: state.descriptionController,
decoration: InputDecoration(
hintText: 'postDescription'.tr(),
border: InputBorder.none,
isCollapsed: true,
contentPadding: const EdgeInsets.fromLTRB(
8,
4,
8,
12,
),
),
style: theme.textTheme.bodyMedium,
minLines: 1,
maxLines: 3,
onTapOutside:
(_) =>
FocusManager.instance.primaryFocus
?.unfocus(),
),
// Content field with borderless design
TextField(
controller: state.contentController, controller: state.contentController,
style: theme.textTheme.bodyMedium, style: theme.textTheme.bodyMedium,
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
hintText: 'postContent'.tr(), hintText: 'postContent'.tr(),
contentPadding: const EdgeInsets.all(8), isCollapsed: true,
contentPadding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 8,
),
), ),
maxLines: null, maxLines: null,
onTapOutside: onTapOutside:
@@ -429,7 +433,6 @@ class PostComposeScreen extends HookConsumerWidget {
FocusManager.instance.primaryFocus FocusManager.instance.primaryFocus
?.unfocus(), ?.unfocus(),
), ),
),
const Gap(8), const Gap(8),
@@ -449,33 +452,14 @@ class PostComposeScreen extends HookConsumerWidget {
), ),
), ),
), ),
),
], ],
).padding(horizontal: 16), ).padding(horizontal: 16),
).alignment(Alignment.topCenter), ).alignment(Alignment.topCenter),
), ),
// Bottom toolbar // Bottom toolbar
Material( ComposeToolbar(state: state, originalPost: originalPost),
elevation: 4,
child: Row(
children: [
IconButton(
onPressed: () => ComposeLogic.pickPhotoMedia(ref, state),
icon: const Icon(Symbols.add_a_photo),
color: colorScheme.primary,
),
IconButton(
onPressed: () => ComposeLogic.pickVideoMedia(ref, state),
icon: const Icon(Symbols.videocam),
color: colorScheme.primary,
),
],
).padding(
bottom: MediaQuery.of(context).padding.bottom + 16,
horizontal: 16,
top: 8,
),
),
], ],
), ),
), ),
@@ -650,7 +634,7 @@ class PostComposeScreen extends HookConsumerWidget {
child: SingleChildScrollView( child: SingleChildScrollView(
controller: scrollController, controller: scrollController,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: PostItem(item: post, isOpenable: false), child: PostItem(item: post),
), ),
), ),
], ],

View File

@@ -19,8 +19,8 @@ import 'package:island/widgets/content/markdown.dart';
import 'package:island/widgets/post/compose_shared.dart'; import 'package:island/widgets/post/compose_shared.dart';
import 'package:island/widgets/post/compose_settings_sheet.dart'; import 'package:island/widgets/post/compose_settings_sheet.dart';
import 'package:island/services/compose_storage_db.dart'; import 'package:island/services/compose_storage_db.dart';
import 'package:island/widgets/post/compose_toolbar.dart';
import 'package:island/widgets/post/publishers_modal.dart'; import 'package:island/widgets/post/publishers_modal.dart';
import 'package:island/widgets/post/draft_manager.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -138,21 +138,62 @@ class ArticleComposeScreen extends HookConsumerWidget {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
builder: builder: (context) => ComposeSettingsSheet(state: state),
(context) => ComposeSettingsSheet(
titleController: state.titleController,
descriptionController: state.descriptionController,
visibility: state.visibility,
tagsController: state.tagsController,
categoriesController: state.categoriesController,
onVisibilityChanged: () {
// Trigger rebuild if needed
},
),
); );
} }
Widget buildPreviewPane() { Widget buildPreviewPane() {
final widgetItem = SingleChildScrollView(
padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 8),
child: ValueListenableBuilder<TextEditingValue>(
valueListenable: state.titleController,
builder: (context, titleValue, _) {
return ValueListenableBuilder<TextEditingValue>(
valueListenable: state.descriptionController,
builder: (context, descriptionValue, _) {
return ValueListenableBuilder<TextEditingValue>(
valueListenable: state.contentController,
builder: (context, contentValue, _) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (titleValue.text.isNotEmpty) ...[
Text(
titleValue.text,
style: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
const Gap(16),
],
if (descriptionValue.text.isNotEmpty) ...[
Text(
descriptionValue.text,
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurface.withOpacity(0.7),
),
),
const Gap(16),
],
if (contentValue.text.isNotEmpty)
MarkdownTextContent(
content: contentValue.text,
textStyle: theme.textTheme.bodyMedium,
),
],
);
},
);
},
);
},
),
);
if (isWideScreen(context)) {
return Align(alignment: Alignment.topLeft, child: widgetItem);
}
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: colorScheme.outline.withOpacity(0.3)), border: Border.all(color: colorScheme.outline.withOpacity(0.3)),
@@ -178,119 +219,52 @@ class ArticleComposeScreen extends HookConsumerWidget {
], ],
), ),
), ),
Expanded( Expanded(child: widgetItem),
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: ValueListenableBuilder<TextEditingValue>(
valueListenable: state.titleController,
builder: (context, titleValue, _) {
return ValueListenableBuilder<TextEditingValue>(
valueListenable: state.descriptionController,
builder: (context, descriptionValue, _) {
return ValueListenableBuilder<TextEditingValue>(
valueListenable: state.contentController,
builder: (context, contentValue, _) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (titleValue.text.isNotEmpty) ...[
Text(
titleValue.text,
style: theme.textTheme.headlineSmall
?.copyWith(fontWeight: FontWeight.bold),
),
const Gap(16),
],
if (descriptionValue.text.isNotEmpty) ...[
Text(
descriptionValue.text,
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurface.withOpacity(
0.7,
),
),
),
const Gap(16),
],
if (contentValue.text.isNotEmpty)
MarkdownTextContent(
content: contentValue.text,
textStyle: theme.textTheme.bodyMedium,
),
],
);
},
);
},
);
},
),
),
),
], ],
), ),
); );
} }
Widget buildEditorPane() { Widget buildEditorPane() {
return Column( return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 560),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Publisher row TextField(
Card( controller: state.titleController,
margin: EdgeInsets.only(top: 8), decoration: InputDecoration(
elevation: 1, hintText: 'postTitle'.tr(),
child: Padding( border: InputBorder.none,
padding: const EdgeInsets.all(12), isCollapsed: true,
child: Row( contentPadding: const EdgeInsets.symmetric(
children: [ vertical: 8,
GestureDetector( horizontal: 8,
child: ProfilePictureWidget(
fileId: state.currentPublisher.value?.picture?.id,
radius: 20,
fallbackIcon:
state.currentPublisher.value == null
? Symbols.question_mark
: null,
), ),
onTap: () {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => const PublisherModal(),
).then((value) {
if (value != null) {
state.currentPublisher.value = value;
}
});
},
), ),
const Gap(16), style: theme.textTheme.titleMedium,
if (state.currentPublisher.value == null) onTapOutside:
Text( (_) => FocusManager.instance.primaryFocus?.unfocus(),
'postPublisherUnselected'.tr(), ),
TextField(
controller: state.descriptionController,
decoration: InputDecoration(
hintText: 'postDescription'.tr(),
border: InputBorder.none,
isCollapsed: true,
contentPadding: const EdgeInsets.fromLTRB(8, 4, 8, 12),
),
style: theme.textTheme.bodyMedium, style: theme.textTheme.bodyMedium,
) minLines: 1,
else maxLines: 3,
Column( onTapOutside:
crossAxisAlignment: CrossAxisAlignment.start, (_) => FocusManager.instance.primaryFocus?.unfocus(),
children: [
Text(state.currentPublisher.value!.nick).bold(),
Text(
'@${state.currentPublisher.value!.name}',
).fontSize(12),
],
), ),
],
),
),
),
// Content field with keyboard listener
Expanded( Expanded(
child: RawKeyboardListener( child: KeyboardListener(
focusNode: FocusNode(), focusNode: FocusNode(),
onKey: onKeyEvent:
(event) => _handleKeyPress( (event) => _handleKeyPress(
event, event,
state, state,
@@ -304,7 +278,10 @@ class ArticleComposeScreen extends HookConsumerWidget {
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
hintText: 'postContent'.tr(), hintText: 'postContent'.tr(),
contentPadding: const EdgeInsets.all(8), contentPadding: const EdgeInsets.symmetric(
vertical: 16,
horizontal: 8,
),
), ),
maxLines: null, maxLines: null,
expands: true, expands: true,
@@ -350,6 +327,13 @@ class ArticleComposeScreen extends HookConsumerWidget {
state, state,
idx, idx,
), ),
onUpdate:
(value) =>
ComposeLogic.updateAttachment(
state,
value,
idx,
),
onDelete: onDelete:
() => ComposeLogic.deleteAttachment( () => ComposeLogic.deleteAttachment(
ref, ref,
@@ -382,6 +366,8 @@ class ArticleComposeScreen extends HookConsumerWidget {
}, },
), ),
], ],
),
),
); );
} }
@@ -392,7 +378,7 @@ class ArticleComposeScreen extends HookConsumerWidget {
} }
}, },
child: AppScaffold( child: AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar( appBar: AppBar(
leading: const PageBackButton(), leading: const PageBackButton(),
title: ValueListenableBuilder<TextEditingValue>( title: ValueListenableBuilder<TextEditingValue>(
@@ -406,39 +392,27 @@ class ArticleComposeScreen extends HookConsumerWidget {
actions: [ actions: [
// Info banner for article compose // Info banner for article compose
const SizedBox.shrink(), const SizedBox.shrink(),
if (originalPost == null) // Only show drafts for new articles
IconButton( IconButton(
icon: const Icon(Symbols.draft), icon: ProfilePictureWidget(
fileId: state.currentPublisher.value?.picture?.id,
radius: 12,
fallbackIcon:
state.currentPublisher.value == null
? Symbols.question_mark
: null,
),
onPressed: () { onPressed: () {
showModalBottomSheet( showModalBottomSheet(
context: context,
isScrollControlled: true, isScrollControlled: true,
builder: context: context,
(context) => DraftManagerSheet( builder: (context) => const PublisherModal(),
onDraftSelected: (draftId) { ).then((value) {
final draft = if (value != null) {
ref.read( state.currentPublisher.value = value;
composeStorageNotifierProvider,
)[draftId];
if (draft != null) {
state.titleController.text = draft.title ?? '';
state.descriptionController.text =
draft.description ?? '';
state.contentController.text =
draft.content ?? '';
state.visibility.value = draft.visibility;
} }
});
}, },
), ),
);
},
tooltip: 'drafts'.tr(),
),
IconButton(
icon: const Icon(Symbols.save),
onPressed: () => ComposeLogic.saveDraft(ref, state),
tooltip: 'saveDraft'.tr(),
),
IconButton( IconButton(
icon: const Icon(Symbols.settings), icon: const Icon(Symbols.settings),
onPressed: showSettingsSheet, onPressed: showSettingsSheet,
@@ -499,6 +473,7 @@ class ArticleComposeScreen extends HookConsumerWidget {
flex: showPreview.value ? 1 : 2, flex: showPreview.value ? 1 : 2,
child: buildEditorPane(), child: buildEditorPane(),
), ),
if (showPreview.value) const VerticalDivider(),
if (showPreview.value) if (showPreview.value)
Expanded(child: buildPreviewPane()), Expanded(child: buildPreviewPane()),
], ],
@@ -510,27 +485,7 @@ class ArticleComposeScreen extends HookConsumerWidget {
), ),
// Bottom toolbar // Bottom toolbar
Material( ComposeToolbar(state: state, originalPost: originalPost),
elevation: 4,
child: Row(
children: [
IconButton(
onPressed: () => ComposeLogic.pickPhotoMedia(ref, state),
icon: const Icon(Symbols.add_a_photo),
color: colorScheme.primary,
),
IconButton(
onPressed: () => ComposeLogic.pickVideoMedia(ref, state),
icon: const Icon(Symbols.videocam),
color: colorScheme.primary,
),
],
).padding(
bottom: MediaQuery.of(context).padding.bottom + 16,
horizontal: 16,
top: 8,
),
),
], ],
), ),
), ),
@@ -539,7 +494,7 @@ class ArticleComposeScreen extends HookConsumerWidget {
// Helper method to handle keyboard shortcuts // Helper method to handle keyboard shortcuts
void _handleKeyPress( void _handleKeyPress(
RawKeyEvent event, KeyEvent event,
ComposeState state, ComposeState state,
WidgetRef ref, WidgetRef ref,
BuildContext context, { BuildContext context, {
@@ -549,7 +504,9 @@ class ArticleComposeScreen extends HookConsumerWidget {
final isPaste = event.logicalKey == LogicalKeyboardKey.keyV; final isPaste = event.logicalKey == LogicalKeyboardKey.keyV;
final isSave = event.logicalKey == LogicalKeyboardKey.keyS; final isSave = event.logicalKey == LogicalKeyboardKey.keyS;
final isModifierPressed = event.isMetaPressed || event.isControlPressed; final isModifierPressed =
HardwareKeyboard.instance.isMetaPressed ||
HardwareKeyboard.instance.isControlPressed;
final isSubmit = event.logicalKey == LogicalKeyboardKey.enter; final isSubmit = event.logicalKey == LogicalKeyboardKey.enter;
if (isPaste && isModifierPressed) { if (isPaste && isModifierPressed) {

View File

@@ -0,0 +1,107 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/post_category.dart';
import 'package:island/models/post_tag.dart';
import 'package:island/pods/network.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/post/post_list.dart';
import 'package:island/widgets/response.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart';
part 'post_category_detail.g.dart';
@riverpod
Future<SnPostCategory> postCategory(Ref ref, String slug) async {
final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get('/sphere/posts/categories/$slug');
return SnPostCategory.fromJson(resp.data);
}
@riverpod
Future<SnPostTag> postTag(Ref ref, String slug) async {
final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get('/sphere/posts/tags/$slug');
return SnPostTag.fromJson(resp.data);
}
class PostCategoryDetailScreen extends HookConsumerWidget {
final String slug;
final bool isCategory;
const PostCategoryDetailScreen({
super.key,
required this.slug,
required this.isCategory,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final postCategory =
isCategory ? ref.watch(postCategoryProvider(slug)) : null;
final postTag = isCategory ? null : ref.watch(postTagProvider(slug));
final postFilterTitle =
isCategory
? postCategory?.value?.categoryDisplayTitle ?? 'loading'
: postTag?.value?.name ?? postTag?.value?.slug ?? 'loading';
return AppScaffold(
isNoBackground: false,
appBar: AppBar(title: Text(postFilterTitle).tr()),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (isCategory)
postCategory!.when(
data:
(category) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(category.categoryDisplayTitle).bold().fontSize(15),
Text('A category'),
],
).padding(horizontal: 24, vertical: 16),
error:
(error, _) => ResponseErrorWidget(
error: error,
onRetry: () => ref.invalidate(postCategoryProvider(slug)),
),
loading: () => ResponseLoadingWidget(),
)
else
postTag!.when(
data:
(tag) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(tag.name ?? '#${tag.slug}').bold().fontSize(15),
Text('A tag'),
],
).padding(horizontal: 24, vertical: 16),
error:
(error, _) => ResponseErrorWidget(
error: error,
onRetry: () => ref.invalidate(postTagProvider(slug)),
),
loading: () => ResponseLoadingWidget(),
),
const Divider(height: 1),
Expanded(
child: CustomScrollView(
slivers: [
const SliverGap(4),
SliverPostList(
categories: isCategory ? [slug] : null,
tags: isCategory ? null : [slug],
),
SliverGap(MediaQuery.of(context).padding.bottom + 8),
],
),
),
],
),
);
}
}

View File

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

View File

@@ -4,7 +4,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/post.dart'; import 'package:island/models/post.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/pods/userinfo.dart';
import 'package:island/services/responsive.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/post/post_item.dart'; import 'package:island/widgets/post/post_item.dart';
import 'package:island/widgets/post/post_quick_reply.dart'; import 'package:island/widgets/post/post_quick_reply.dart';
@@ -54,10 +53,8 @@ class PostDetailScreen extends HookConsumerWidget {
final postState = ref.watch(postStateProvider(id)); final postState = ref.watch(postStateProvider(id));
final user = ref.watch(userInfoProvider); final user = ref.watch(userInfoProvider);
final isWide = isWideScreen(context);
return AppScaffold( return AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar(title: const Text('Post')), appBar: AppBar(title: const Text('Post')),
body: postState.when( body: postState.when(
data: (post) { data: (post) {
@@ -67,13 +64,13 @@ class PostDetailScreen extends HookConsumerWidget {
CustomScrollView( CustomScrollView(
slivers: [ slivers: [
SliverToBoxAdapter( SliverToBoxAdapter(
child: Column( child: Center(
children: [ child: ConstrainedBox(
PostItem( constraints: BoxConstraints(maxWidth: 600),
child: PostItem(
item: post!, item: post!,
isOpenable: false,
isFullPost: true, isFullPost: true,
backgroundColor: isWide ? Colors.transparent : null, isEmbedReply: false,
onUpdate: (newItem) { onUpdate: (newItem) {
// Update the local state with the new post data // Update the local state with the new post data
ref ref
@@ -81,11 +78,10 @@ class PostDetailScreen extends HookConsumerWidget {
.updatePost(newItem); .updatePost(newItem);
}, },
), ),
const Divider(height: 1),
],
), ),
), ),
PostRepliesList(postId: id), ),
PostRepliesList(postId: id, maxWidth: 600),
SliverGap(MediaQuery.of(context).padding.bottom + 80), SliverGap(MediaQuery.of(context).padding.bottom + 80),
], ],
), ),

View File

@@ -5,6 +5,7 @@ import 'package:island/models/post.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/post/post_item.dart'; import 'package:island/widgets/post/post_item.dart';
import 'package:island/widgets/response.dart';
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
final postSearchNotifierProvider = StateNotifierProvider.autoDispose< final postSearchNotifierProvider = StateNotifierProvider.autoDispose<
@@ -55,7 +56,7 @@ class PostSearchNotifier
'query': _currentQuery, 'query': _currentQuery,
'offset': offset, 'offset': offset,
'take': _pageSize, 'take': _pageSize,
'useVector': true, 'useVector': false,
}, },
); );
@@ -109,7 +110,7 @@ class _PostSearchScreenState extends ConsumerState<PostSearchScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar( appBar: AppBar(
title: TextField( title: TextField(
controller: _searchController, controller: _searchController,
@@ -141,6 +142,7 @@ class _PostSearchScreenState extends ConsumerState<PostSearchScreen> {
} }
return ListView.builder( return ListView.builder(
padding: EdgeInsets.zero,
itemCount: data.items.length + (data.hasMore ? 1 : 0), itemCount: data.items.length + (data.hasMore ? 1 : 0),
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (index >= data.items.length) { if (index >= data.items.length) {
@@ -151,14 +153,27 @@ class _PostSearchScreenState extends ConsumerState<PostSearchScreen> {
} }
final post = data.items[index]; final post = data.items[index];
return Column( return Center(
children: [PostItem(item: post), const Divider(height: 1)], child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 600),
child: Card(
margin: EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
child: PostActionableItem(item: post, borderRadius: 8),
),
),
); );
}, },
); );
}, },
loading: () => const Center(child: CircularProgressIndicator()), loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text('Error: $error')), error:
(error, stack) => ResponseErrorWidget(
error: error,
onRetry: () => ref.invalidate(postSearchNotifierProvider),
),
); );
}, },
), ),

View File

@@ -11,12 +11,14 @@ import 'package:island/models/user.dart';
import 'package:island/pods/config.dart'; import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/services/color.dart'; import 'package:island/services/color.dart';
import 'package:island/services/responsive.dart';
import 'package:island/widgets/account/account_name.dart'; import 'package:island/widgets/account/account_name.dart';
import 'package:island/widgets/account/badge.dart'; import 'package:island/widgets/account/badge.dart';
import 'package:island/widgets/account/status.dart'; import 'package:island/widgets/account/status.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/content/markdown.dart';
import 'package:island/widgets/post/post_list.dart'; import 'package:island/widgets/post/post_list.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:palette_generator/palette_generator.dart'; import 'package:palette_generator/palette_generator.dart';
@@ -85,13 +87,22 @@ class PublisherProfileScreen extends HookConsumerWidget {
publisherAppbarForcegroundColorProvider(name), publisherAppbarForcegroundColorProvider(name),
); );
final categoryTabController = useTabController(initialLength: 3);
final categoryTab = useState(0);
categoryTabController.addListener(() {
categoryTab.value = categoryTabController.index;
});
final subscribing = useState(false); final subscribing = useState(false);
Future<void> subscribe() async { Future<void> subscribe() async {
final apiClient = ref.watch(apiClientProvider); final apiClient = ref.watch(apiClientProvider);
subscribing.value = true; subscribing.value = true;
try { try {
await apiClient.post("/publishers/$name/subscribe", data: {'tier': 0}); await apiClient.post(
"/sphere/publishers/$name/subscribe",
data: {'tier': 0},
);
ref.invalidate(publisherSubscriptionStatusProvider(name)); ref.invalidate(publisherSubscriptionStatusProvider(name));
HapticFeedback.heavyImpact(); HapticFeedback.heavyImpact();
} catch (err) { } catch (err) {
@@ -105,7 +116,7 @@ class PublisherProfileScreen extends HookConsumerWidget {
final apiClient = ref.watch(apiClientProvider); final apiClient = ref.watch(apiClientProvider);
subscribing.value = true; subscribing.value = true;
try { try {
await apiClient.post("/publishers/$name/unsubscribe"); await apiClient.post("/sphere/publishers/$name/unsubscribe");
ref.invalidate(publisherSubscriptionStatusProvider(name)); ref.invalidate(publisherSubscriptionStatusProvider(name));
HapticFeedback.heavyImpact(); HapticFeedback.heavyImpact();
} catch (err) { } catch (err) {
@@ -121,51 +132,7 @@ class PublisherProfileScreen extends HookConsumerWidget {
offset: Offset(1.0, 1.0), offset: Offset(1.0, 1.0),
); );
return publisher.when( Widget publisherBasisWidget(SnPublisher data) => Row(
data:
(data) => AppScaffold(
noBackground: false,
body: CustomScrollView(
slivers: [
SliverAppBar(
foregroundColor: appbarColor.value,
expandedHeight: 180,
pinned: true,
leading: PageBackButton(
color: appbarColor.value,
shadows: [appbarShadow],
),
flexibleSpace: Stack(
children: [
Positioned.fill(
child:
data.background?.id != null
? CloudImageWidget(file: data.background)
: Container(
color:
Theme.of(
context,
).appBarTheme.backgroundColor,
),
),
FlexibleSpaceBar(
title: Text(
data.nick,
style: TextStyle(
color:
appbarColor.value ??
Theme.of(context).appBarTheme.foregroundColor,
shadows: [appbarShadow],
),
),
background:
Container(), // Empty container since background is handled by Stack
),
],
),
),
SliverToBoxAdapter(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 20, spacing: 20,
children: [ children: [
@@ -178,13 +145,9 @@ class PublisherProfileScreen extends HookConsumerWidget {
size: 16, size: 16,
color: Theme.of(context).colorScheme.onPrimary, color: Theme.of(context).colorScheme.onPrimary,
), ),
backgroundColor: backgroundColor: Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.primary,
offset: Offset(0, 48), offset: Offset(0, 48),
child: ProfilePictureWidget( child: ProfilePictureWidget(file: data.picture, radius: 32),
file: data.picture,
radius: 32,
),
), ),
onTap: () { onTap: () {
Navigator.pop(context, true); Navigator.pop(context, true);
@@ -206,9 +169,13 @@ class PublisherProfileScreen extends HookConsumerWidget {
Text(data.nick).fontSize(20), Text(data.nick).fontSize(20),
if (data.verification != null) if (data.verification != null)
VerificationMark(mark: data.verification!), VerificationMark(mark: data.verification!),
Text( Expanded(
child: Text(
'@${data.name}', '@${data.name}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
).fontSize(14).opacity(0.85), ).fontSize(14).opacity(0.85),
),
], ],
), ),
if (data.type == 0 && data.account != null) if (data.type == 0 && data.account != null)
@@ -217,16 +184,12 @@ class PublisherProfileScreen extends HookConsumerWidget {
spacing: 6, spacing: 6,
children: [ children: [
Icon( Icon(
data.type == 0 data.type == 0 ? Symbols.person : Symbols.workspaces,
? Symbols.person
: Symbols.workspaces,
fill: 1, fill: 1,
size: 17, size: 17,
), ),
Text( Text(
'publisherBelongsTo'.tr( 'publisherBelongsTo'.tr(args: ['@${data.account!.name}']),
args: ['@${data.account!.name}'],
),
).fontSize(14), ).fontSize(14),
], ],
).opacity(0.85).padding(bottom: 6), ).opacity(0.85).padding(bottom: 6),
@@ -257,9 +220,7 @@ class PublisherProfileScreen extends HookConsumerWidget {
: 'subscribe', : 'subscribe',
).tr(), ).tr(),
style: ButtonStyle( style: ButtonStyle(
visualDensity: VisualDensity( visualDensity: VisualDensity(vertical: -2),
vertical: -2,
),
), ),
), ),
error: (_, _) => const SizedBox(), error: (_, _) => const SizedBox(),
@@ -270,9 +231,7 @@ class PublisherProfileScreen extends HookConsumerWidget {
child: SizedBox( child: SizedBox(
width: 20, width: 20,
height: 20, height: 20,
child: CircularProgressIndicator( child: CircularProgressIndicator(strokeWidth: 2),
strokeWidth: 2,
),
), ),
), ),
), ),
@@ -282,49 +241,216 @@ class PublisherProfileScreen extends HookConsumerWidget {
), ),
), ),
], ],
).padding(horizontal: 24, top: 24), ).padding(horizontal: 24, top: 24);
),
SliverToBoxAdapter( Widget publisherBadgesWidget(SnPublisher data) =>
child: Column( (badges.value?.isNotEmpty ?? false)
children: [ ? Card(
if (badges.value?.isNotEmpty ?? false) child: BadgeList(
BadgeList(badges: badges.value!).padding(top: 16), badges: badges.value!,
if (data.verification != null) ).padding(horizontal: 26, vertical: 20),
VerificationStatusCard( ).padding(horizontal: 4)
mark: data.verification!, : const SizedBox.shrink();
).padding(top: 16),
], Widget publisherVerificationWidget(SnPublisher data) =>
).padding(horizontal: 24), (data.verification != null)
), ? Card(
SliverToBoxAdapter( margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: const Divider(height: 1).padding(vertical: 24), child: VerificationStatusCard(mark: data.verification!),
), )
SliverToBoxAdapter( : const SizedBox.shrink();
Widget publisherBioWidget(SnPublisher data) => Card(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
Text('bio').tr().bold(), Text('bio').tr().bold().fontSize(15).padding(bottom: 8),
Text( if (data.bio.isEmpty)
data.bio.isEmpty ? 'descriptionNone'.tr() : data.bio, Text('descriptionNone').tr().italic()
else
MarkdownTextContent(
content: data.bio,
linesMargin: EdgeInsets.zero,
), ),
], ],
).padding(horizontal: 24), ).padding(horizontal: 20, vertical: 16),
);
Widget publisherCategoryTabWidget() => Card(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: TabBar(
controller: categoryTabController,
dividerColor: Colors.transparent,
splashBorderRadius: const BorderRadius.all(Radius.circular(8)),
tabs: [Tab(text: 'All'), Tab(text: 'Posts'), Tab(text: 'Articles')],
),
);
return publisher.when(
data:
(data) => AppScaffold(
isNoBackground: false,
appBar:
isWideScreen(context)
? AppBar(
foregroundColor: appbarColor.value,
leading: PageBackButton(
color: appbarColor.value,
shadows: [appbarShadow],
),
flexibleSpace: Stack(
children: [
Positioned.fill(
child:
data.background?.id != null
? CloudImageWidget(file: data.background)
: Container(
color:
Theme.of(
context,
).appBarTheme.backgroundColor,
),
),
FlexibleSpaceBar(
title: Text(
data.nick,
style: TextStyle(
color:
appbarColor.value ??
Theme.of(
context,
).appBarTheme.foregroundColor,
shadows: [appbarShadow],
),
),
background:
Container(), // Empty container since background is handled by Stack
),
],
),
)
: null,
body:
isWideScreen(context)
? Row(
children: [
Flexible(
flex: 4,
child: CustomScrollView(
slivers: [
SliverGap(16),
SliverToBoxAdapter(
child: publisherCategoryTabWidget(),
),
SliverPostList(
key: ValueKey(categoryTab.value),
pubName: name,
type: switch (categoryTab.value) {
1 => 0,
2 => 1,
_ => null,
},
),
SliverGap(
MediaQuery.of(context).padding.bottom + 16,
),
],
).padding(left: 8),
),
Flexible(
flex: 3,
child: Align(
alignment: Alignment.topLeft,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
publisherBasisWidget(data).padding(bottom: 8),
publisherBadgesWidget(data),
publisherVerificationWidget(data),
publisherBioWidget(data),
],
),
),
),
),
],
)
: CustomScrollView(
slivers: [
SliverAppBar(
foregroundColor: appbarColor.value,
expandedHeight: 180,
pinned: true,
leading: PageBackButton(
color: appbarColor.value,
shadows: [appbarShadow],
),
flexibleSpace: Stack(
children: [
Positioned.fill(
child:
data.background?.id != null
? CloudImageWidget(
file: data.background,
)
: Container(
color:
Theme.of(
context,
).appBarTheme.backgroundColor,
),
),
FlexibleSpaceBar(
title: Text(
data.nick,
style: TextStyle(
color:
appbarColor.value ??
Theme.of(
context,
).appBarTheme.foregroundColor,
shadows: [appbarShadow],
),
),
background:
Container(), // Empty container since background is handled by Stack
),
],
),
), ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: const Divider(height: 1).padding(top: 24), child: publisherBasisWidget(data).padding(bottom: 8),
),
SliverToBoxAdapter(child: publisherBadgesWidget(data)),
SliverToBoxAdapter(
child: publisherVerificationWidget(data),
),
SliverToBoxAdapter(child: publisherBioWidget(data)),
SliverToBoxAdapter(child: publisherCategoryTabWidget()),
SliverPostList(
key: ValueKey(categoryTab.value),
pubName: name,
type: switch (categoryTab.value) {
1 => 0,
2 => 1,
_ => null,
},
), ),
SliverPostList(pubName: name),
SliverGap(MediaQuery.of(context).padding.bottom + 16), SliverGap(MediaQuery.of(context).padding.bottom + 16),
], ],
), ),
), ),
error: error:
(error, stackTrace) => AppScaffold( (error, stackTrace) => AppScaffold(
isNoBackground: false,
appBar: AppBar(leading: const PageBackButton()), appBar: AppBar(leading: const PageBackButton()),
body: Center(child: Text(error.toString())), body: Center(child: Text(error.toString())),
), ),
loading: loading:
() => AppScaffold( () => AppScaffold(
isNoBackground: false,
appBar: AppBar(leading: const PageBackButton()), appBar: AppBar(leading: const PageBackButton()),
body: Center(child: CircularProgressIndicator()), body: Center(child: CircularProgressIndicator()),
), ),

View File

@@ -79,7 +79,7 @@ class RealmDetailScreen extends HookConsumerWidget {
); );
return AppScaffold( return AppScaffold(
noBackground: false, isNoBackground: false,
body: realmState.when( body: realmState.when(
loading: () => const Center(child: CircularProgressIndicator()), loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => Center(child: Text('Error: $error')), error: (error, _) => Center(child: Text('Error: $error')),
@@ -321,10 +321,10 @@ class _RealmActionMenu extends HookConsumerWidget {
showConfirmAlert( showConfirmAlert(
'leaveRealmHint'.tr(), 'leaveRealmHint'.tr(),
'leaveRealm'.tr(), 'leaveRealm'.tr(),
).then((confirm) { ).then((confirm) async {
if (confirm) { if (confirm) {
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
client.delete( await client.delete(
'/sphere/realms/$realmSlug/members/me', '/sphere/realms/$realmSlug/members/me',
); );
ref.invalidate(realmsJoinedProvider); ref.invalidate(realmsJoinedProvider);
@@ -361,10 +361,12 @@ class _RealmActionMenu extends HookConsumerWidget {
showConfirmAlert( showConfirmAlert(
'leaveRealmHint'.tr(), 'leaveRealmHint'.tr(),
'leaveRealm'.tr(), 'leaveRealm'.tr(),
).then((confirm) { ).then((confirm) async {
if (confirm) { if (confirm) {
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
client.delete('/sphere/realms/$realmSlug/members/me'); await client.delete(
'/sphere/realms/$realmSlug/members/me',
);
ref.invalidate(realmsJoinedProvider); ref.invalidate(realmsJoinedProvider);
if (context.mounted) { if (context.mounted) {
context.pop(true); context.pop(true);

View File

@@ -41,7 +41,7 @@ class RealmListScreen extends HookConsumerWidget {
final realmInvites = ref.watch(realmInvitesProvider); final realmInvites = ref.watch(realmInvitesProvider);
return AppScaffold( return AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar( appBar: AppBar(
title: const Text('realms').tr(), title: const Text('realms').tr(),
actions: [ actions: [
@@ -279,7 +279,7 @@ class EditRealmScreen extends HookConsumerWidget {
} }
return AppScaffold( return AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar( appBar: AppBar(
title: Text(slug == null ? 'createRealm'.tr() : 'editRealm'.tr()), title: Text(slug == null ? 'createRealm'.tr() : 'editRealm'.tr()),
leading: const PageBackButton(), leading: const PageBackButton(),

View File

@@ -552,7 +552,7 @@ class SettingsScreen extends HookConsumerWidget {
} }
return AppScaffold( return AppScaffold(
noBackground: false, isNoBackground: false,
appBar: AppBar( appBar: AppBar(
title: Text('settings').tr(), title: Text('settings').tr(),
actions: actions:

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