Compare commits
	
		
			10 Commits
		
	
	
		
			3.0.0+108
			...
			666a2dfbf5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 666a2dfbf5 | |||
| fd979c3a35 | |||
| 847fc6e864 | |||
| 356b7bf01a | |||
| 450d5ebc81 | |||
| f04285848f | |||
| c4becb0a05 | |||
| d22619396b | |||
| fe8640a6db | |||
| ff475d43dd | 
							
								
								
									
										5
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							@@ -3,7 +3,7 @@ name: Build Release
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    tags:
 | 
			
		||||
      - '*'
 | 
			
		||||
      - "*"
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
@@ -59,6 +59,7 @@ jobs:
 | 
			
		||||
          sudo apt-get install -y libnotify-dev
 | 
			
		||||
          sudo apt-get install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
 | 
			
		||||
          sudo apt-get install -y gstreamer-1.0
 | 
			
		||||
          sudo apt-get install -y libsecret-1-0
 | 
			
		||||
      - run: flutter pub get
 | 
			
		||||
      - run: flutter build linux
 | 
			
		||||
      - name: Archive production artifacts
 | 
			
		||||
@@ -80,4 +81,4 @@ jobs:
 | 
			
		||||
        uses: actions/upload-artifact@v4
 | 
			
		||||
        with:
 | 
			
		||||
          name: build-output-linux-appimage
 | 
			
		||||
          path: './*.AppImage*'
 | 
			
		||||
          path: "./*.AppImage*"
 | 
			
		||||
 
 | 
			
		||||
@@ -98,7 +98,7 @@
 | 
			
		||||
        <receiver
 | 
			
		||||
            android:name=".receiver.ReplyReceiver"
 | 
			
		||||
            android:enabled="true"
 | 
			
		||||
            android:exported="false" />
 | 
			
		||||
            android:exported="true" />
 | 
			
		||||
 | 
			
		||||
        <service
 | 
			
		||||
            android:name=".service.MessagingService"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,39 @@
 | 
			
		||||
package dev.solsynth.solian
 | 
			
		||||
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import io.flutter.embedding.android.FlutterActivity
 | 
			
		||||
import io.flutter.embedding.engine.FlutterEngine
 | 
			
		||||
import io.flutter.plugin.common.MethodChannel
 | 
			
		||||
import io.flutter.plugins.sharedpreferences.LegacySharedPreferencesPlugin
 | 
			
		||||
 | 
			
		||||
class MainActivity : FlutterActivity()
 | 
			
		||||
{
 | 
			
		||||
    private val CHANNEL = "dev.solsynth.solian/notifications"
 | 
			
		||||
 | 
			
		||||
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
 | 
			
		||||
        super.configureFlutterEngine(flutterEngine)
 | 
			
		||||
        // https://github.com/flutter/flutter/issues/153075#issuecomment-2693189362
 | 
			
		||||
        flutterEngine.plugins.add(LegacySharedPreferencesPlugin())
 | 
			
		||||
 | 
			
		||||
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
 | 
			
		||||
            if (call.method == "initialLink") {
 | 
			
		||||
                val roomId = intent.getStringExtra("room_id")
 | 
			
		||||
                if (roomId != null) {
 | 
			
		||||
                    result.success("/rooms/$roomId")
 | 
			
		||||
                } else {
 | 
			
		||||
                    result.success(null)
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                result.notImplemented()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onNewIntent(intent: Intent) {
 | 
			
		||||
        super.onNewIntent(intent)
 | 
			
		||||
        val roomId = intent.getStringExtra("room_id")
 | 
			
		||||
        if (roomId != null) {
 | 
			
		||||
            MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, CHANNEL).invokeMethod("newLink", "/rooms/$roomId")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,47 +1,46 @@
 | 
			
		||||
package dev.solsynth.solian.network
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import okhttp3.*
 | 
			
		||||
import android.content.SharedPreferences
 | 
			
		||||
import okhttp3.Call
 | 
			
		||||
import okhttp3.Callback
 | 
			
		||||
import okhttp3.MediaType.Companion.toMediaType
 | 
			
		||||
import okhttp3.OkHttpClient
 | 
			
		||||
import okhttp3.Request
 | 
			
		||||
import okhttp3.RequestBody.Companion.toRequestBody
 | 
			
		||||
import okhttp3.Response
 | 
			
		||||
import org.json.JSONObject
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
 | 
			
		||||
class ApiClient(private val context: Context) {
 | 
			
		||||
    private val client = OkHttpClient()
 | 
			
		||||
    private val sharedPreferences: SharedPreferences = context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
 | 
			
		||||
 | 
			
		||||
    fun sendMessage(roomId: String, content: String, repliedMessageId: String, callback: () -> Unit) {
 | 
			
		||||
        val prefs = context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
 | 
			
		||||
        val token = prefs.getString("flutter.token", null)
 | 
			
		||||
        val serverUrl = prefs.getString("flutter.serverUrl", null)
 | 
			
		||||
 | 
			
		||||
        if (token == null || serverUrl == null) {
 | 
			
		||||
    fun sendMessage(roomId: String, message: String, replyTo: String, callback: (Boolean) -> Unit) {
 | 
			
		||||
        val token = sharedPreferences.getString("flutter.token", null)
 | 
			
		||||
        if (token == null) {
 | 
			
		||||
            callback(false)
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val url = "$serverUrl/chat/$roomId/messages"
 | 
			
		||||
 | 
			
		||||
        val json = JSONObject()
 | 
			
		||||
        json.put("content", content)
 | 
			
		||||
        json.put("replied_message_id", repliedMessageId)
 | 
			
		||||
 | 
			
		||||
        val requestBody = json.toString().toRequestBody("application/json; charset=utf-8".toMediaType())
 | 
			
		||||
 | 
			
		||||
        val json = JSONObject().apply {
 | 
			
		||||
            put("content", message)
 | 
			
		||||
            put("reply_to", replyTo)
 | 
			
		||||
        }
 | 
			
		||||
        val body = json.toString().toRequestBody("application/json; charset=utf-8".toMediaType())
 | 
			
		||||
        val request = Request.Builder()
 | 
			
		||||
            .url(url)
 | 
			
		||||
            .post(requestBody)
 | 
			
		||||
            .addHeader("Authorization", "AtField $token")
 | 
			
		||||
            .url("https://solian.dev/api/rooms/$roomId/messages")
 | 
			
		||||
            .header("Authorization", "Bearer $token")
 | 
			
		||||
            .post(body)
 | 
			
		||||
            .build()
 | 
			
		||||
 | 
			
		||||
        client.newCall(request).enqueue(object : Callback {
 | 
			
		||||
            override fun onFailure(call: Call, e: IOException) {
 | 
			
		||||
                // Handle failure
 | 
			
		||||
                callback()
 | 
			
		||||
                callback(false)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            override fun onResponse(call: Call, response: Response) {
 | 
			
		||||
                // Handle success
 | 
			
		||||
                callback()
 | 
			
		||||
                callback(response.isSuccessful)
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -72,6 +72,7 @@ class MessagingService: FirebaseMessagingService() {
 | 
			
		||||
 | 
			
		||||
        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")
 | 
			
		||||
 
 | 
			
		||||
@@ -89,14 +89,32 @@
 | 
			
		||||
  "authFactorInAppNotifyDescription": "A one-time code sent via in-app notification.",
 | 
			
		||||
  "authFactorPin": "Pin Code",
 | 
			
		||||
  "authFactorPinDescription": "It consists of 6 digits. It cannot be used to log in. When performing some dangerous operations, the system will ask you to enter this PIN for confirmation.",
 | 
			
		||||
  "realms": "Realms",
 | 
			
		||||
  "createRealm": "Create a Realm",
 | 
			
		||||
  "createRealmHint": "Meet friends with same interests, build communities, and more.",
 | 
			
		||||
  "editRealm": "Edit Realm",
 | 
			
		||||
  "deleteRealm": "Delete Realm",
 | 
			
		||||
  "deleteRealmHint": "Are you sure to delete this realm? This will also deleted all the channels, publishers, and posts under this realm.",
 | 
			
		||||
  "explore": "Explore",
 | 
			
		||||
  "exploreFilterSubscriptions": "Subscriptions",
 | 
			
		||||
  "exploreFilterFriends": "Friends",
 | 
			
		||||
  "discover": "Discover",
 | 
			
		||||
  "joinRealm": "Join Realm",
 | 
			
		||||
  "account": "Account",
 | 
			
		||||
  "name": "Name",
 | 
			
		||||
  "slug": "Slug",
 | 
			
		||||
  "slugHint": "The slug will be used in the URL to access this resource, it should be unique and URL safe.",
 | 
			
		||||
  "createChatRoom": "Create a Room",
 | 
			
		||||
  "editChatRoom": "Edit Room",
 | 
			
		||||
  "deleteChatRoom": "Delete Room",
 | 
			
		||||
  "deleteChatRoomHint": "Are you sure to delete this room? This action cannot be undone.",
 | 
			
		||||
  "chat": "Chat",
 | 
			
		||||
  "chatTabAll": "All",
 | 
			
		||||
  "chatTabDirect": "Direct Messages",
 | 
			
		||||
  "chatTabGroup": "Group Chats",
 | 
			
		||||
  "chatMessageHint": "Message in {}",
 | 
			
		||||
  "chatDirectMessageHint": "Message to {}",
 | 
			
		||||
  "directMessage": "Direct Message",
 | 
			
		||||
  "loading": "Loading...",
 | 
			
		||||
  "descriptionNone": "No description yet.",
 | 
			
		||||
  "invites": "Invites",
 | 
			
		||||
@@ -231,6 +249,7 @@
 | 
			
		||||
  "uploadingProgress": "Uploading {} of {}",
 | 
			
		||||
  "uploadAll": "Upload All",
 | 
			
		||||
  "stickerCopyPlaceholder": "Copy Placeholder",
 | 
			
		||||
  "realmSelection": "Select a Realm",
 | 
			
		||||
  "individual": "Individual",
 | 
			
		||||
  "firstPostBadgeName": "First Post",
 | 
			
		||||
  "firstPostBadgeDescription": "Created your first post on Solar Network",
 | 
			
		||||
@@ -286,6 +305,12 @@
 | 
			
		||||
  "levelingProgressExperience": "{} EXP",
 | 
			
		||||
  "levelingProgressLevel": "Level {}",
 | 
			
		||||
  "fileUploadingProgress": "Uploading file #{}: {}%",
 | 
			
		||||
  "removeChatMember": "Remove Chat Room Member",
 | 
			
		||||
  "removeChatMemberHint": "Are you sure to remove this member from the room?",
 | 
			
		||||
  "removeRealmMember": "Remove Realm Member",
 | 
			
		||||
  "removeRealmMemberHint": "Are you sure to remove this member from the realm?",
 | 
			
		||||
  "removePublisherMember": "Remove Publisher Member",
 | 
			
		||||
  "removePublisherMemberHint": "Are you sure to remove this member from the publisher?",
 | 
			
		||||
  "memberRole": "Member Role",
 | 
			
		||||
  "memberRoleHint": "Greater number has higher permission.",
 | 
			
		||||
  "memberRoleEdit": "Edit role for @{}",
 | 
			
		||||
@@ -293,6 +318,10 @@
 | 
			
		||||
  "openLinkConfirmDescription": "You're going to leave the Solar Network and open the link ({}) in your browser. It is not related to Solar Network. Beware of phishing and scams.",
 | 
			
		||||
  "brokenLink": "Unable open link {}... It might be broken or missing uri parts...",
 | 
			
		||||
  "copyToClipboard": "Copy to clipboard",
 | 
			
		||||
  "leaveChatRoom": "Leave Chat Room",
 | 
			
		||||
  "leaveChatRoomHint": "Are you sure to leave this chat room?",
 | 
			
		||||
  "leaveRealm": "Leave Realm",
 | 
			
		||||
  "leaveRealmHint": "Are you sure to leave this realm?",
 | 
			
		||||
  "walletNotFound": "Wallet not found",
 | 
			
		||||
  "walletCreateHint": "You don't have a wallet yet. Create one to start using the Solar Network eWallet.",
 | 
			
		||||
  "walletCreate": "Create a Wallet",
 | 
			
		||||
@@ -304,6 +333,12 @@
 | 
			
		||||
  "settingsBackgroundImageClear": "Clear Background Image",
 | 
			
		||||
  "settingsBackgroundGenerateColor": "Generate color scheme from Bacground Image",
 | 
			
		||||
  "messageNone": "No content to display",
 | 
			
		||||
  "unreadMessages": {
 | 
			
		||||
    "one": "{} unread message",
 | 
			
		||||
    "other": "{} unread messages"
 | 
			
		||||
  },
 | 
			
		||||
  "chatBreakNone": "None",
 | 
			
		||||
  "settingsRealmCompactView": "Compact Realm View",
 | 
			
		||||
  "settingsMixedFeed": "Mixed Feed",
 | 
			
		||||
  "settingsAutoTranslate": "Auto Translate",
 | 
			
		||||
  "settingsHideBottomNav": "Hide Bottom Navigation",
 | 
			
		||||
@@ -346,6 +381,7 @@
 | 
			
		||||
  "postVisibilityUnlisted": "Unlisted",
 | 
			
		||||
  "postVisibilityPrivate": "Private",
 | 
			
		||||
  "postTruncated": "Content truncated, tap to view full post",
 | 
			
		||||
  "copyMessage": "Copy Message",
 | 
			
		||||
  "authFactor": "Authentication Factor",
 | 
			
		||||
  "authFactorDelete": "Delete the Factor",
 | 
			
		||||
  "authFactorDeleteHint": "Are you sure you want to delete this authentication factor? This action cannot be undone.",
 | 
			
		||||
@@ -373,6 +409,10 @@
 | 
			
		||||
  "lastActiveAt": "Last active at {}",
 | 
			
		||||
  "authDeviceLogout": "Logout",
 | 
			
		||||
  "authDeviceLogoutHint": "Are you sure you want to logout this device? This will also disable the push notification to this device.",
 | 
			
		||||
  "typingHint": {
 | 
			
		||||
    "one": "{} is typing...",
 | 
			
		||||
    "other": "{} are typing..."
 | 
			
		||||
  },
 | 
			
		||||
  "authDeviceEditLabel": "Edit Label",
 | 
			
		||||
  "authDeviceLabelTitle": "Edit Device Label",
 | 
			
		||||
  "authDeviceLabelHint": "Enter a name for this device",
 | 
			
		||||
@@ -439,6 +479,21 @@
 | 
			
		||||
  "contactMethodSetPrimary": "Set as Primary",
 | 
			
		||||
  "contactMethodSetPrimaryHint": "Set this contact method as your primary contact method for account recovery and notifications",
 | 
			
		||||
  "contactMethodDeleteHint": "Are you sure to delete this contact method? This action cannot be undone.",
 | 
			
		||||
  "chatNotifyLevel": "Notify Level",
 | 
			
		||||
  "chatNotifyLevelDescription": "Decide how many notifications you will receive.",
 | 
			
		||||
  "chatNotifyLevelAll": "All",
 | 
			
		||||
  "chatNotifyLevelMention": "Mentions",
 | 
			
		||||
  "chatNotifyLevelNone": "None",
 | 
			
		||||
  "chatNotifyLevelUpdated": "The notify level has been updated to {}.",
 | 
			
		||||
  "chatBreak": "Take a Break",
 | 
			
		||||
  "chatBreakDescription": "Set a time, before that time, your notification level will be metions only, to take a break of the current topic they're talking about.",
 | 
			
		||||
  "chatBreakClear": "Clear the break time",
 | 
			
		||||
  "chatBreakHour": "{} break",
 | 
			
		||||
  "chatBreakDay": "{} day break",
 | 
			
		||||
  "chatBreakSet": "Break set for {}",
 | 
			
		||||
  "chatBreakCleared": "Chat break has been cleared.",
 | 
			
		||||
  "chatBreakCustom": "Custom duration",
 | 
			
		||||
  "chatBreakEnterMinutes": "Enter minutes",
 | 
			
		||||
  "firstName": "First Name",
 | 
			
		||||
  "middleName": "Middle Name",
 | 
			
		||||
  "lastName": "Last Name",
 | 
			
		||||
@@ -520,17 +575,29 @@
 | 
			
		||||
  "quickActions": "Quick Actions",
 | 
			
		||||
  "post": "Post",
 | 
			
		||||
  "copy": "Copy",
 | 
			
		||||
  "sendToChat": "Send to Chat",
 | 
			
		||||
  "failedToShareToPost": "Failed to share to post: {}",
 | 
			
		||||
  "shareToChatComingSoon": "Share to chat functionality coming soon",
 | 
			
		||||
  "failedToShareToChat": "Failed to share to chat: {}",
 | 
			
		||||
  "shareToSpecificChatComingSoon": "Share to {} coming soon",
 | 
			
		||||
  "directChat": "Direct Chat",
 | 
			
		||||
  "systemShareComingSoon": "System share functionality coming soon",
 | 
			
		||||
  "failedToShareToSystem": "Failed to share to system: {}",
 | 
			
		||||
  "failedToCopy": "Failed to copy: {}",
 | 
			
		||||
  "noChatRoomsAvailable": "No chat rooms available",
 | 
			
		||||
  "failedToLoadChats": "Failed to load chats",
 | 
			
		||||
  "contentToShare": "Content to share:",
 | 
			
		||||
  "unknownChat": "Unknown Chat",
 | 
			
		||||
  "addAdditionalMessage": "Add additional message...",
 | 
			
		||||
  "uploadingFiles": "Uploading files...",
 | 
			
		||||
  "sharedSuccessfully": "Shared successfully!",
 | 
			
		||||
  "shareSuccess": "Shared successfully!",
 | 
			
		||||
  "shareToSpecificChatSuccess": "Shared to {} successfully!",
 | 
			
		||||
  "wouldYouLikeToGoToChat": "Would you like to go to the chat?",
 | 
			
		||||
  "no": "No",
 | 
			
		||||
  "yes": "Yes",
 | 
			
		||||
  "navigateToChat": "Navigate to Chat",
 | 
			
		||||
  "wouldYouLikeToNavigateToChat": "Would you like to navigate to the chat?",
 | 
			
		||||
  "abuseReport": "Report",
 | 
			
		||||
  "abuseReportTitle": "Report Content",
 | 
			
		||||
  "abuseReportDescription": "Help us keep the community safe by reporting inappropriate content or behavior.",
 | 
			
		||||
@@ -564,5 +631,49 @@
 | 
			
		||||
  "realmJoinSuccess": "Successfully joined the realm.",
 | 
			
		||||
  "discoverRealms": "Discover Realms",
 | 
			
		||||
  "discoverPublishers": "Discover Publishers",
 | 
			
		||||
  "search": "Search"
 | 
			
		||||
  "search": "Search",
 | 
			
		||||
  "publisherMembers": "Collaborators",
 | 
			
		||||
  "developerHub": "Developer Hub",
 | 
			
		||||
  "developerHubUnselectedHint": "Select a developer to see stats or enroll a new one.",
 | 
			
		||||
  "enrollDeveloper": "Enroll as a Developer",
 | 
			
		||||
  "enrollDeveloperHint": "Enroll one of your publishers to become a developer.",
 | 
			
		||||
  "noPublishersToEnroll": "You don't have any publishers that can be enrolled as a developer.",
 | 
			
		||||
  "totalCustomApps": "Total Custom Apps",
 | 
			
		||||
  "customApps": "Custom Apps",
 | 
			
		||||
  "noCustomApps": "No custom apps yet.",
 | 
			
		||||
  "createCustomApp": "Create Custom App",
 | 
			
		||||
  "editCustomApp": "Edit Custom App",
 | 
			
		||||
  "deleteCustomApp": "Delete Custom App",
 | 
			
		||||
  "deleteCustomAppHint": "Are you sure you want to delete this custom app? This action cannot be undone.",
 | 
			
		||||
  "publicRealm": "Public Realm",
 | 
			
		||||
  "publicRealmDescription": "Anyone can preview the content of this realm.",
 | 
			
		||||
  "communityRealm": "Community Realm",
 | 
			
		||||
  "communityRealmDescription": "Anyone can join this realm and participate in discussions. And will show in the discover page & feed.",
 | 
			
		||||
  "publicChat": "Public Chat",
 | 
			
		||||
  "publicChatDescription": "Anyone can preview the content of this chat. Including unjoined bots.",
 | 
			
		||||
  "communityChat": "Community Chat",
 | 
			
		||||
  "communityChatDescription": "Anyone can join this chat and participate in discussions.",
 | 
			
		||||
  "appLinks": "App Links",
 | 
			
		||||
  "homePageUrl": "Home Page URL",
 | 
			
		||||
  "privacyPolicyUrl": "Privacy Policy URL",
 | 
			
		||||
  "termsOfServiceUrl": "Terms of Service URL",
 | 
			
		||||
  "oauthConfig": "OAuth Configuration",
 | 
			
		||||
  "clientUri": "Client URI",
 | 
			
		||||
  "redirectUris": "Redirect URIs",
 | 
			
		||||
  "addRedirectUri": "Add Redirect URI",
 | 
			
		||||
  "allowedScopes": "Allowed Scopes",
 | 
			
		||||
  "requirePkce": "Require PKCE",
 | 
			
		||||
  "allowOfflineAccess": "Allow Offline Access",
 | 
			
		||||
  "redirectUri": "Redirect URI",
 | 
			
		||||
  "redirectUriHint": "The redirect URI is used for OAuth authentication. When the app goes to production, we will validate the redirect URI is match your configuration to reject invalid requests.",
 | 
			
		||||
  "uriRequired": "The URI is required.",
 | 
			
		||||
  "uriInvalid": "The URI is invalid.",
 | 
			
		||||
  "add": "Add",
 | 
			
		||||
  "addScope": "Add Scope",
 | 
			
		||||
  "scope": "Scope",
 | 
			
		||||
  "publisherFeatures": "Features",
 | 
			
		||||
  "publisherFeatureDevelop": "Developer Program",
 | 
			
		||||
  "publisherFeatureDevelopDescription": "Unlock development abilities for your publisher, including custom apps, API keys, and more.",
 | 
			
		||||
  "publisherFeatureDevelopHint": "Currently, this feature is under active development, you need send a request to unlock this feature.",
 | 
			
		||||
  "learnMore": "Learn More"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import 'package:firebase_core/firebase_core.dart';
 | 
			
		||||
import 'package:firebase_messaging/firebase_messaging.dart';
 | 
			
		||||
import 'package:flutter/foundation.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter/services.dart';
 | 
			
		||||
import 'package:flutter_hooks/flutter_hooks.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:image_picker_android/image_picker_android.dart';
 | 
			
		||||
@@ -158,6 +159,28 @@ class IslandApp extends HookConsumerWidget {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    useEffect(() {
 | 
			
		||||
      const channel = MethodChannel('dev.solsynth.solian/notifications');
 | 
			
		||||
 | 
			
		||||
      Future<void> handleInitialLink() async {
 | 
			
		||||
        final String? link = await channel.invokeMethod('initialLink');
 | 
			
		||||
        if (link != null) {
 | 
			
		||||
          final router = ref.read(routerProvider);
 | 
			
		||||
          router.go(link);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!kIsWeb && Platform.isAndroid) {
 | 
			
		||||
        handleInitialLink();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      channel.setMethodCallHandler((call) async {
 | 
			
		||||
        if (call.method == 'newLink') {
 | 
			
		||||
          final String link = call.arguments;
 | 
			
		||||
          final router = ref.read(routerProvider);
 | 
			
		||||
          router.go(link);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // When the app is opened from a terminated state.
 | 
			
		||||
      FirebaseMessaging.instance.getInitialMessage().then((message) {
 | 
			
		||||
        if (message != null) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										71
									
								
								lib/models/custom_app.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								lib/models/custom_app.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
import 'package:freezed_annotation/freezed_annotation.dart';
 | 
			
		||||
import 'package:island/models/file.dart';
 | 
			
		||||
import 'package:island/models/user.dart';
 | 
			
		||||
 | 
			
		||||
part 'custom_app.freezed.dart';
 | 
			
		||||
part 'custom_app.g.dart';
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
sealed class CustomApp with _$CustomApp {
 | 
			
		||||
  const factory CustomApp({
 | 
			
		||||
    @Default('') String id,
 | 
			
		||||
    @Default('') String slug,
 | 
			
		||||
    @Default('') String name,
 | 
			
		||||
    String? description,
 | 
			
		||||
    @Default(0) int status,
 | 
			
		||||
    SnCloudFile? picture,
 | 
			
		||||
    SnCloudFile? background,
 | 
			
		||||
    SnVerificationMark? verification,
 | 
			
		||||
    CustomAppOauthConfig? oauthConfig,
 | 
			
		||||
    CustomAppLinks? links,
 | 
			
		||||
    @Default([]) List<CustomAppSecret> secrets,
 | 
			
		||||
    @Default('') String publisherId,
 | 
			
		||||
  }) = _CustomApp;
 | 
			
		||||
 | 
			
		||||
  factory CustomApp.fromJson(Map<String, dynamic> json) =>
 | 
			
		||||
      _$CustomAppFromJson(json);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
sealed class CustomAppLinks with _$CustomAppLinks {
 | 
			
		||||
  const factory CustomAppLinks({
 | 
			
		||||
    String? homePage,
 | 
			
		||||
    String? privacyPolicy,
 | 
			
		||||
    String? termsOfService,
 | 
			
		||||
  }) = _CustomAppLinks;
 | 
			
		||||
 | 
			
		||||
  factory CustomAppLinks.fromJson(Map<String, dynamic> json) =>
 | 
			
		||||
      _$CustomAppLinksFromJson(json);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
sealed class CustomAppOauthConfig with _$CustomAppOauthConfig {
 | 
			
		||||
  const factory CustomAppOauthConfig({
 | 
			
		||||
    String? clientUri,
 | 
			
		||||
    @Default([]) List<String> redirectUris,
 | 
			
		||||
    List<String>? postLogoutRedirectUris,
 | 
			
		||||
    @Default(['openid', 'profile', 'email']) List<String> allowedScopes,
 | 
			
		||||
    @Default(['authorization_code', 'refresh_token'])
 | 
			
		||||
    List<String> allowedGrantTypes,
 | 
			
		||||
    @Default(true) bool requirePkce,
 | 
			
		||||
    @Default(false) bool allowOfflineAccess,
 | 
			
		||||
  }) = _CustomAppOauthConfig;
 | 
			
		||||
 | 
			
		||||
  factory CustomAppOauthConfig.fromJson(Map<String, dynamic> json) =>
 | 
			
		||||
      _$CustomAppOauthConfigFromJson(json);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
sealed class CustomAppSecret with _$CustomAppSecret {
 | 
			
		||||
  const factory CustomAppSecret({
 | 
			
		||||
    @Default('') String id,
 | 
			
		||||
    @Default('') String secret,
 | 
			
		||||
    String? description,
 | 
			
		||||
    DateTime? expiredAt,
 | 
			
		||||
    @Default(false) bool isOidc,
 | 
			
		||||
    @Default('') String appId,
 | 
			
		||||
  }) = _CustomAppSecret;
 | 
			
		||||
 | 
			
		||||
  factory CustomAppSecret.fromJson(Map<String, dynamic> json) =>
 | 
			
		||||
      _$CustomAppSecretFromJson(json);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										771
									
								
								lib/models/custom_app.freezed.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										771
									
								
								lib/models/custom_app.freezed.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,771 @@
 | 
			
		||||
// dart format width=80
 | 
			
		||||
// coverage:ignore-file
 | 
			
		||||
// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
			
		||||
// ignore_for_file: type=lint
 | 
			
		||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
 | 
			
		||||
 | 
			
		||||
part of 'custom_app.dart';
 | 
			
		||||
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
// FreezedGenerator
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
 | 
			
		||||
// dart format off
 | 
			
		||||
T _$identity<T>(T value) => value;
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
mixin _$CustomApp {
 | 
			
		||||
 | 
			
		||||
 String get id; String get slug; String get name; String? get description; int get status; SnCloudFile? get picture; SnCloudFile? get background; SnVerificationMark? get verification; CustomAppOauthConfig? get oauthConfig; CustomAppLinks? get links; List<CustomAppSecret> get secrets; String get publisherId;
 | 
			
		||||
/// Create a copy of CustomApp
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$CustomAppCopyWith<CustomApp> get copyWith => _$CustomAppCopyWithImpl<CustomApp>(this as CustomApp, _$identity);
 | 
			
		||||
 | 
			
		||||
  /// Serializes this CustomApp to a JSON map.
 | 
			
		||||
  Map<String, dynamic> toJson();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is CustomApp&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.status, status) || other.status == status)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.oauthConfig, oauthConfig) || other.oauthConfig == oauthConfig)&&(identical(other.links, links) || other.links == links)&&const DeepCollectionEquality().equals(other.secrets, secrets)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,id,slug,name,description,status,picture,background,verification,oauthConfig,links,const DeepCollectionEquality().hash(secrets),publisherId);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'CustomApp(id: $id, slug: $slug, name: $name, description: $description, status: $status, picture: $picture, background: $background, verification: $verification, oauthConfig: $oauthConfig, links: $links, secrets: $secrets, publisherId: $publisherId)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class $CustomAppCopyWith<$Res>  {
 | 
			
		||||
  factory $CustomAppCopyWith(CustomApp value, $Res Function(CustomApp) _then) = _$CustomAppCopyWithImpl;
 | 
			
		||||
@useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String id, String slug, String name, String? description, int status, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, CustomAppOauthConfig? oauthConfig, CustomAppLinks? links, List<CustomAppSecret> secrets, String publisherId
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
$SnCloudFileCopyWith<$Res>? get picture;$SnCloudFileCopyWith<$Res>? get background;$SnVerificationMarkCopyWith<$Res>? get verification;$CustomAppOauthConfigCopyWith<$Res>? get oauthConfig;$CustomAppLinksCopyWith<$Res>? get links;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class _$CustomAppCopyWithImpl<$Res>
 | 
			
		||||
    implements $CustomAppCopyWith<$Res> {
 | 
			
		||||
  _$CustomAppCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final CustomApp _self;
 | 
			
		||||
  final $Res Function(CustomApp) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of CustomApp
 | 
			
		||||
/// 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 = null,Object? description = freezed,Object? status = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? oauthConfig = freezed,Object? links = freezed,Object? secrets = null,Object? publisherId = null,}) {
 | 
			
		||||
  return _then(_self.copyWith(
 | 
			
		||||
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,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?,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as int,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnCloudFile?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnVerificationMark?,oauthConfig: freezed == oauthConfig ? _self.oauthConfig : oauthConfig // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as CustomAppOauthConfig?,links: freezed == links ? _self.links : links // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as CustomAppLinks?,secrets: null == secrets ? _self.secrets : secrets // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as List<CustomAppSecret>,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
/// Create a copy of CustomApp
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnCloudFileCopyWith<$Res>? get picture {
 | 
			
		||||
    if (_self.picture == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnCloudFileCopyWith<$Res>(_self.picture!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(picture: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of CustomApp
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnCloudFileCopyWith<$Res>? get background {
 | 
			
		||||
    if (_self.background == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnCloudFileCopyWith<$Res>(_self.background!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(background: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of CustomApp
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnVerificationMarkCopyWith<$Res>? get verification {
 | 
			
		||||
    if (_self.verification == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(verification: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of CustomApp
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$CustomAppOauthConfigCopyWith<$Res>? get oauthConfig {
 | 
			
		||||
    if (_self.oauthConfig == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $CustomAppOauthConfigCopyWith<$Res>(_self.oauthConfig!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(oauthConfig: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of CustomApp
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$CustomAppLinksCopyWith<$Res>? get links {
 | 
			
		||||
    if (_self.links == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $CustomAppLinksCopyWith<$Res>(_self.links!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(links: value));
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
@JsonSerializable()
 | 
			
		||||
 | 
			
		||||
class _CustomApp implements CustomApp {
 | 
			
		||||
  const _CustomApp({this.id = '', this.slug = '', this.name = '', this.description, this.status = 0, this.picture, this.background, this.verification, this.oauthConfig, this.links, final  List<CustomAppSecret> secrets = const [], this.publisherId = ''}): _secrets = secrets;
 | 
			
		||||
  factory _CustomApp.fromJson(Map<String, dynamic> json) => _$CustomAppFromJson(json);
 | 
			
		||||
 | 
			
		||||
@override@JsonKey() final  String id;
 | 
			
		||||
@override@JsonKey() final  String slug;
 | 
			
		||||
@override@JsonKey() final  String name;
 | 
			
		||||
@override final  String? description;
 | 
			
		||||
@override@JsonKey() final  int status;
 | 
			
		||||
@override final  SnCloudFile? picture;
 | 
			
		||||
@override final  SnCloudFile? background;
 | 
			
		||||
@override final  SnVerificationMark? verification;
 | 
			
		||||
@override final  CustomAppOauthConfig? oauthConfig;
 | 
			
		||||
@override final  CustomAppLinks? links;
 | 
			
		||||
 final  List<CustomAppSecret> _secrets;
 | 
			
		||||
@override@JsonKey() List<CustomAppSecret> get secrets {
 | 
			
		||||
  if (_secrets is EqualUnmodifiableListView) return _secrets;
 | 
			
		||||
  // ignore: implicit_dynamic_type
 | 
			
		||||
  return EqualUnmodifiableListView(_secrets);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@override@JsonKey() final  String publisherId;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of CustomApp
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
_$CustomAppCopyWith<_CustomApp> get copyWith => __$CustomAppCopyWithImpl<_CustomApp>(this, _$identity);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
Map<String, dynamic> toJson() {
 | 
			
		||||
  return _$CustomAppToJson(this, );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is _CustomApp&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.status, status) || other.status == status)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.oauthConfig, oauthConfig) || other.oauthConfig == oauthConfig)&&(identical(other.links, links) || other.links == links)&&const DeepCollectionEquality().equals(other._secrets, _secrets)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,id,slug,name,description,status,picture,background,verification,oauthConfig,links,const DeepCollectionEquality().hash(_secrets),publisherId);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'CustomApp(id: $id, slug: $slug, name: $name, description: $description, status: $status, picture: $picture, background: $background, verification: $verification, oauthConfig: $oauthConfig, links: $links, secrets: $secrets, publisherId: $publisherId)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class _$CustomAppCopyWith<$Res> implements $CustomAppCopyWith<$Res> {
 | 
			
		||||
  factory _$CustomAppCopyWith(_CustomApp value, $Res Function(_CustomApp) _then) = __$CustomAppCopyWithImpl;
 | 
			
		||||
@override @useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String id, String slug, String name, String? description, int status, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, CustomAppOauthConfig? oauthConfig, CustomAppLinks? links, List<CustomAppSecret> secrets, String publisherId
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@override $SnCloudFileCopyWith<$Res>? get picture;@override $SnCloudFileCopyWith<$Res>? get background;@override $SnVerificationMarkCopyWith<$Res>? get verification;@override $CustomAppOauthConfigCopyWith<$Res>? get oauthConfig;@override $CustomAppLinksCopyWith<$Res>? get links;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class __$CustomAppCopyWithImpl<$Res>
 | 
			
		||||
    implements _$CustomAppCopyWith<$Res> {
 | 
			
		||||
  __$CustomAppCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final _CustomApp _self;
 | 
			
		||||
  final $Res Function(_CustomApp) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of CustomApp
 | 
			
		||||
/// 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 = null,Object? description = freezed,Object? status = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? oauthConfig = freezed,Object? links = freezed,Object? secrets = null,Object? publisherId = null,}) {
 | 
			
		||||
  return _then(_CustomApp(
 | 
			
		||||
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,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?,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as int,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnCloudFile?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnVerificationMark?,oauthConfig: freezed == oauthConfig ? _self.oauthConfig : oauthConfig // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as CustomAppOauthConfig?,links: freezed == links ? _self.links : links // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as CustomAppLinks?,secrets: null == secrets ? _self._secrets : secrets // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as List<CustomAppSecret>,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create a copy of CustomApp
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnCloudFileCopyWith<$Res>? get picture {
 | 
			
		||||
    if (_self.picture == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnCloudFileCopyWith<$Res>(_self.picture!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(picture: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of CustomApp
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnCloudFileCopyWith<$Res>? get background {
 | 
			
		||||
    if (_self.background == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnCloudFileCopyWith<$Res>(_self.background!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(background: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of CustomApp
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnVerificationMarkCopyWith<$Res>? get verification {
 | 
			
		||||
    if (_self.verification == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(verification: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of CustomApp
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$CustomAppOauthConfigCopyWith<$Res>? get oauthConfig {
 | 
			
		||||
    if (_self.oauthConfig == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $CustomAppOauthConfigCopyWith<$Res>(_self.oauthConfig!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(oauthConfig: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of CustomApp
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$CustomAppLinksCopyWith<$Res>? get links {
 | 
			
		||||
    if (_self.links == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $CustomAppLinksCopyWith<$Res>(_self.links!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(links: value));
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
mixin _$CustomAppLinks {
 | 
			
		||||
 | 
			
		||||
 String? get homePage; String? get privacyPolicy; String? get termsOfService;
 | 
			
		||||
/// Create a copy of CustomAppLinks
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$CustomAppLinksCopyWith<CustomAppLinks> get copyWith => _$CustomAppLinksCopyWithImpl<CustomAppLinks>(this as CustomAppLinks, _$identity);
 | 
			
		||||
 | 
			
		||||
  /// Serializes this CustomAppLinks to a JSON map.
 | 
			
		||||
  Map<String, dynamic> toJson();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is CustomAppLinks&&(identical(other.homePage, homePage) || other.homePage == homePage)&&(identical(other.privacyPolicy, privacyPolicy) || other.privacyPolicy == privacyPolicy)&&(identical(other.termsOfService, termsOfService) || other.termsOfService == termsOfService));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,homePage,privacyPolicy,termsOfService);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'CustomAppLinks(homePage: $homePage, privacyPolicy: $privacyPolicy, termsOfService: $termsOfService)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class $CustomAppLinksCopyWith<$Res>  {
 | 
			
		||||
  factory $CustomAppLinksCopyWith(CustomAppLinks value, $Res Function(CustomAppLinks) _then) = _$CustomAppLinksCopyWithImpl;
 | 
			
		||||
@useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String? homePage, String? privacyPolicy, String? termsOfService
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class _$CustomAppLinksCopyWithImpl<$Res>
 | 
			
		||||
    implements $CustomAppLinksCopyWith<$Res> {
 | 
			
		||||
  _$CustomAppLinksCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final CustomAppLinks _self;
 | 
			
		||||
  final $Res Function(CustomAppLinks) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of CustomAppLinks
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@pragma('vm:prefer-inline') @override $Res call({Object? homePage = freezed,Object? privacyPolicy = freezed,Object? termsOfService = freezed,}) {
 | 
			
		||||
  return _then(_self.copyWith(
 | 
			
		||||
homePage: freezed == homePage ? _self.homePage : homePage // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,privacyPolicy: freezed == privacyPolicy ? _self.privacyPolicy : privacyPolicy // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,termsOfService: freezed == termsOfService ? _self.termsOfService : termsOfService // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
@JsonSerializable()
 | 
			
		||||
 | 
			
		||||
class _CustomAppLinks implements CustomAppLinks {
 | 
			
		||||
  const _CustomAppLinks({this.homePage, this.privacyPolicy, this.termsOfService});
 | 
			
		||||
  factory _CustomAppLinks.fromJson(Map<String, dynamic> json) => _$CustomAppLinksFromJson(json);
 | 
			
		||||
 | 
			
		||||
@override final  String? homePage;
 | 
			
		||||
@override final  String? privacyPolicy;
 | 
			
		||||
@override final  String? termsOfService;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of CustomAppLinks
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
_$CustomAppLinksCopyWith<_CustomAppLinks> get copyWith => __$CustomAppLinksCopyWithImpl<_CustomAppLinks>(this, _$identity);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
Map<String, dynamic> toJson() {
 | 
			
		||||
  return _$CustomAppLinksToJson(this, );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is _CustomAppLinks&&(identical(other.homePage, homePage) || other.homePage == homePage)&&(identical(other.privacyPolicy, privacyPolicy) || other.privacyPolicy == privacyPolicy)&&(identical(other.termsOfService, termsOfService) || other.termsOfService == termsOfService));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,homePage,privacyPolicy,termsOfService);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'CustomAppLinks(homePage: $homePage, privacyPolicy: $privacyPolicy, termsOfService: $termsOfService)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class _$CustomAppLinksCopyWith<$Res> implements $CustomAppLinksCopyWith<$Res> {
 | 
			
		||||
  factory _$CustomAppLinksCopyWith(_CustomAppLinks value, $Res Function(_CustomAppLinks) _then) = __$CustomAppLinksCopyWithImpl;
 | 
			
		||||
@override @useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String? homePage, String? privacyPolicy, String? termsOfService
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class __$CustomAppLinksCopyWithImpl<$Res>
 | 
			
		||||
    implements _$CustomAppLinksCopyWith<$Res> {
 | 
			
		||||
  __$CustomAppLinksCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final _CustomAppLinks _self;
 | 
			
		||||
  final $Res Function(_CustomAppLinks) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of CustomAppLinks
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @pragma('vm:prefer-inline') $Res call({Object? homePage = freezed,Object? privacyPolicy = freezed,Object? termsOfService = freezed,}) {
 | 
			
		||||
  return _then(_CustomAppLinks(
 | 
			
		||||
homePage: freezed == homePage ? _self.homePage : homePage // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,privacyPolicy: freezed == privacyPolicy ? _self.privacyPolicy : privacyPolicy // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,termsOfService: freezed == termsOfService ? _self.termsOfService : termsOfService // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
mixin _$CustomAppOauthConfig {
 | 
			
		||||
 | 
			
		||||
 String? get clientUri; List<String> get redirectUris; List<String>? get postLogoutRedirectUris; List<String> get allowedScopes; List<String> get allowedGrantTypes; bool get requirePkce; bool get allowOfflineAccess;
 | 
			
		||||
/// Create a copy of CustomAppOauthConfig
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$CustomAppOauthConfigCopyWith<CustomAppOauthConfig> get copyWith => _$CustomAppOauthConfigCopyWithImpl<CustomAppOauthConfig>(this as CustomAppOauthConfig, _$identity);
 | 
			
		||||
 | 
			
		||||
  /// Serializes this CustomAppOauthConfig to a JSON map.
 | 
			
		||||
  Map<String, dynamic> toJson();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is CustomAppOauthConfig&&(identical(other.clientUri, clientUri) || other.clientUri == clientUri)&&const DeepCollectionEquality().equals(other.redirectUris, redirectUris)&&const DeepCollectionEquality().equals(other.postLogoutRedirectUris, postLogoutRedirectUris)&&const DeepCollectionEquality().equals(other.allowedScopes, allowedScopes)&&const DeepCollectionEquality().equals(other.allowedGrantTypes, allowedGrantTypes)&&(identical(other.requirePkce, requirePkce) || other.requirePkce == requirePkce)&&(identical(other.allowOfflineAccess, allowOfflineAccess) || other.allowOfflineAccess == allowOfflineAccess));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,clientUri,const DeepCollectionEquality().hash(redirectUris),const DeepCollectionEquality().hash(postLogoutRedirectUris),const DeepCollectionEquality().hash(allowedScopes),const DeepCollectionEquality().hash(allowedGrantTypes),requirePkce,allowOfflineAccess);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'CustomAppOauthConfig(clientUri: $clientUri, redirectUris: $redirectUris, postLogoutRedirectUris: $postLogoutRedirectUris, allowedScopes: $allowedScopes, allowedGrantTypes: $allowedGrantTypes, requirePkce: $requirePkce, allowOfflineAccess: $allowOfflineAccess)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class $CustomAppOauthConfigCopyWith<$Res>  {
 | 
			
		||||
  factory $CustomAppOauthConfigCopyWith(CustomAppOauthConfig value, $Res Function(CustomAppOauthConfig) _then) = _$CustomAppOauthConfigCopyWithImpl;
 | 
			
		||||
@useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String? clientUri, List<String> redirectUris, List<String>? postLogoutRedirectUris, List<String> allowedScopes, List<String> allowedGrantTypes, bool requirePkce, bool allowOfflineAccess
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class _$CustomAppOauthConfigCopyWithImpl<$Res>
 | 
			
		||||
    implements $CustomAppOauthConfigCopyWith<$Res> {
 | 
			
		||||
  _$CustomAppOauthConfigCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final CustomAppOauthConfig _self;
 | 
			
		||||
  final $Res Function(CustomAppOauthConfig) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of CustomAppOauthConfig
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@pragma('vm:prefer-inline') @override $Res call({Object? clientUri = freezed,Object? redirectUris = null,Object? postLogoutRedirectUris = freezed,Object? allowedScopes = null,Object? allowedGrantTypes = null,Object? requirePkce = null,Object? allowOfflineAccess = null,}) {
 | 
			
		||||
  return _then(_self.copyWith(
 | 
			
		||||
clientUri: freezed == clientUri ? _self.clientUri : clientUri // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,redirectUris: null == redirectUris ? _self.redirectUris : redirectUris // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as List<String>,postLogoutRedirectUris: freezed == postLogoutRedirectUris ? _self.postLogoutRedirectUris : postLogoutRedirectUris // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as List<String>?,allowedScopes: null == allowedScopes ? _self.allowedScopes : allowedScopes // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as List<String>,allowedGrantTypes: null == allowedGrantTypes ? _self.allowedGrantTypes : allowedGrantTypes // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as List<String>,requirePkce: null == requirePkce ? _self.requirePkce : requirePkce // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as bool,allowOfflineAccess: null == allowOfflineAccess ? _self.allowOfflineAccess : allowOfflineAccess // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as bool,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
@JsonSerializable()
 | 
			
		||||
 | 
			
		||||
class _CustomAppOauthConfig implements CustomAppOauthConfig {
 | 
			
		||||
  const _CustomAppOauthConfig({this.clientUri, final  List<String> redirectUris = const [], final  List<String>? postLogoutRedirectUris, final  List<String> allowedScopes = const ['openid', 'profile', 'email'], final  List<String> allowedGrantTypes = const ['authorization_code', 'refresh_token'], this.requirePkce = true, this.allowOfflineAccess = false}): _redirectUris = redirectUris,_postLogoutRedirectUris = postLogoutRedirectUris,_allowedScopes = allowedScopes,_allowedGrantTypes = allowedGrantTypes;
 | 
			
		||||
  factory _CustomAppOauthConfig.fromJson(Map<String, dynamic> json) => _$CustomAppOauthConfigFromJson(json);
 | 
			
		||||
 | 
			
		||||
@override final  String? clientUri;
 | 
			
		||||
 final  List<String> _redirectUris;
 | 
			
		||||
@override@JsonKey() List<String> get redirectUris {
 | 
			
		||||
  if (_redirectUris is EqualUnmodifiableListView) return _redirectUris;
 | 
			
		||||
  // ignore: implicit_dynamic_type
 | 
			
		||||
  return EqualUnmodifiableListView(_redirectUris);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 final  List<String>? _postLogoutRedirectUris;
 | 
			
		||||
@override List<String>? get postLogoutRedirectUris {
 | 
			
		||||
  final value = _postLogoutRedirectUris;
 | 
			
		||||
  if (value == null) return null;
 | 
			
		||||
  if (_postLogoutRedirectUris is EqualUnmodifiableListView) return _postLogoutRedirectUris;
 | 
			
		||||
  // ignore: implicit_dynamic_type
 | 
			
		||||
  return EqualUnmodifiableListView(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 final  List<String> _allowedScopes;
 | 
			
		||||
@override@JsonKey() List<String> get allowedScopes {
 | 
			
		||||
  if (_allowedScopes is EqualUnmodifiableListView) return _allowedScopes;
 | 
			
		||||
  // ignore: implicit_dynamic_type
 | 
			
		||||
  return EqualUnmodifiableListView(_allowedScopes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 final  List<String> _allowedGrantTypes;
 | 
			
		||||
@override@JsonKey() List<String> get allowedGrantTypes {
 | 
			
		||||
  if (_allowedGrantTypes is EqualUnmodifiableListView) return _allowedGrantTypes;
 | 
			
		||||
  // ignore: implicit_dynamic_type
 | 
			
		||||
  return EqualUnmodifiableListView(_allowedGrantTypes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@override@JsonKey() final  bool requirePkce;
 | 
			
		||||
@override@JsonKey() final  bool allowOfflineAccess;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of CustomAppOauthConfig
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
_$CustomAppOauthConfigCopyWith<_CustomAppOauthConfig> get copyWith => __$CustomAppOauthConfigCopyWithImpl<_CustomAppOauthConfig>(this, _$identity);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
Map<String, dynamic> toJson() {
 | 
			
		||||
  return _$CustomAppOauthConfigToJson(this, );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is _CustomAppOauthConfig&&(identical(other.clientUri, clientUri) || other.clientUri == clientUri)&&const DeepCollectionEquality().equals(other._redirectUris, _redirectUris)&&const DeepCollectionEquality().equals(other._postLogoutRedirectUris, _postLogoutRedirectUris)&&const DeepCollectionEquality().equals(other._allowedScopes, _allowedScopes)&&const DeepCollectionEquality().equals(other._allowedGrantTypes, _allowedGrantTypes)&&(identical(other.requirePkce, requirePkce) || other.requirePkce == requirePkce)&&(identical(other.allowOfflineAccess, allowOfflineAccess) || other.allowOfflineAccess == allowOfflineAccess));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,clientUri,const DeepCollectionEquality().hash(_redirectUris),const DeepCollectionEquality().hash(_postLogoutRedirectUris),const DeepCollectionEquality().hash(_allowedScopes),const DeepCollectionEquality().hash(_allowedGrantTypes),requirePkce,allowOfflineAccess);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'CustomAppOauthConfig(clientUri: $clientUri, redirectUris: $redirectUris, postLogoutRedirectUris: $postLogoutRedirectUris, allowedScopes: $allowedScopes, allowedGrantTypes: $allowedGrantTypes, requirePkce: $requirePkce, allowOfflineAccess: $allowOfflineAccess)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class _$CustomAppOauthConfigCopyWith<$Res> implements $CustomAppOauthConfigCopyWith<$Res> {
 | 
			
		||||
  factory _$CustomAppOauthConfigCopyWith(_CustomAppOauthConfig value, $Res Function(_CustomAppOauthConfig) _then) = __$CustomAppOauthConfigCopyWithImpl;
 | 
			
		||||
@override @useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String? clientUri, List<String> redirectUris, List<String>? postLogoutRedirectUris, List<String> allowedScopes, List<String> allowedGrantTypes, bool requirePkce, bool allowOfflineAccess
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class __$CustomAppOauthConfigCopyWithImpl<$Res>
 | 
			
		||||
    implements _$CustomAppOauthConfigCopyWith<$Res> {
 | 
			
		||||
  __$CustomAppOauthConfigCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final _CustomAppOauthConfig _self;
 | 
			
		||||
  final $Res Function(_CustomAppOauthConfig) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of CustomAppOauthConfig
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @pragma('vm:prefer-inline') $Res call({Object? clientUri = freezed,Object? redirectUris = null,Object? postLogoutRedirectUris = freezed,Object? allowedScopes = null,Object? allowedGrantTypes = null,Object? requirePkce = null,Object? allowOfflineAccess = null,}) {
 | 
			
		||||
  return _then(_CustomAppOauthConfig(
 | 
			
		||||
clientUri: freezed == clientUri ? _self.clientUri : clientUri // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,redirectUris: null == redirectUris ? _self._redirectUris : redirectUris // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as List<String>,postLogoutRedirectUris: freezed == postLogoutRedirectUris ? _self._postLogoutRedirectUris : postLogoutRedirectUris // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as List<String>?,allowedScopes: null == allowedScopes ? _self._allowedScopes : allowedScopes // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as List<String>,allowedGrantTypes: null == allowedGrantTypes ? _self._allowedGrantTypes : allowedGrantTypes // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as List<String>,requirePkce: null == requirePkce ? _self.requirePkce : requirePkce // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as bool,allowOfflineAccess: null == allowOfflineAccess ? _self.allowOfflineAccess : allowOfflineAccess // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as bool,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
mixin _$CustomAppSecret {
 | 
			
		||||
 | 
			
		||||
 String get id; String get secret; String? get description; DateTime? get expiredAt; bool get isOidc; String get appId;
 | 
			
		||||
/// Create a copy of CustomAppSecret
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$CustomAppSecretCopyWith<CustomAppSecret> get copyWith => _$CustomAppSecretCopyWithImpl<CustomAppSecret>(this as CustomAppSecret, _$identity);
 | 
			
		||||
 | 
			
		||||
  /// Serializes this CustomAppSecret to a JSON map.
 | 
			
		||||
  Map<String, dynamic> toJson();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is CustomAppSecret&&(identical(other.id, id) || other.id == id)&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.description, description) || other.description == description)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&(identical(other.isOidc, isOidc) || other.isOidc == isOidc)&&(identical(other.appId, appId) || other.appId == appId));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,id,secret,description,expiredAt,isOidc,appId);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'CustomAppSecret(id: $id, secret: $secret, description: $description, expiredAt: $expiredAt, isOidc: $isOidc, appId: $appId)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class $CustomAppSecretCopyWith<$Res>  {
 | 
			
		||||
  factory $CustomAppSecretCopyWith(CustomAppSecret value, $Res Function(CustomAppSecret) _then) = _$CustomAppSecretCopyWithImpl;
 | 
			
		||||
@useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String id, String secret, String? description, DateTime? expiredAt, bool isOidc, String appId
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class _$CustomAppSecretCopyWithImpl<$Res>
 | 
			
		||||
    implements $CustomAppSecretCopyWith<$Res> {
 | 
			
		||||
  _$CustomAppSecretCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final CustomAppSecret _self;
 | 
			
		||||
  final $Res Function(CustomAppSecret) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of CustomAppSecret
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? secret = null,Object? description = freezed,Object? expiredAt = freezed,Object? isOidc = null,Object? appId = null,}) {
 | 
			
		||||
  return _then(_self.copyWith(
 | 
			
		||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,secret: null == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,isOidc: null == isOidc ? _self.isOidc : isOidc // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as bool,appId: null == appId ? _self.appId : appId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
@JsonSerializable()
 | 
			
		||||
 | 
			
		||||
class _CustomAppSecret implements CustomAppSecret {
 | 
			
		||||
  const _CustomAppSecret({this.id = '', this.secret = '', this.description, this.expiredAt, this.isOidc = false, this.appId = ''});
 | 
			
		||||
  factory _CustomAppSecret.fromJson(Map<String, dynamic> json) => _$CustomAppSecretFromJson(json);
 | 
			
		||||
 | 
			
		||||
@override@JsonKey() final  String id;
 | 
			
		||||
@override@JsonKey() final  String secret;
 | 
			
		||||
@override final  String? description;
 | 
			
		||||
@override final  DateTime? expiredAt;
 | 
			
		||||
@override@JsonKey() final  bool isOidc;
 | 
			
		||||
@override@JsonKey() final  String appId;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of CustomAppSecret
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
_$CustomAppSecretCopyWith<_CustomAppSecret> get copyWith => __$CustomAppSecretCopyWithImpl<_CustomAppSecret>(this, _$identity);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
Map<String, dynamic> toJson() {
 | 
			
		||||
  return _$CustomAppSecretToJson(this, );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is _CustomAppSecret&&(identical(other.id, id) || other.id == id)&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.description, description) || other.description == description)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&(identical(other.isOidc, isOidc) || other.isOidc == isOidc)&&(identical(other.appId, appId) || other.appId == appId));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,id,secret,description,expiredAt,isOidc,appId);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'CustomAppSecret(id: $id, secret: $secret, description: $description, expiredAt: $expiredAt, isOidc: $isOidc, appId: $appId)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class _$CustomAppSecretCopyWith<$Res> implements $CustomAppSecretCopyWith<$Res> {
 | 
			
		||||
  factory _$CustomAppSecretCopyWith(_CustomAppSecret value, $Res Function(_CustomAppSecret) _then) = __$CustomAppSecretCopyWithImpl;
 | 
			
		||||
@override @useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String id, String secret, String? description, DateTime? expiredAt, bool isOidc, String appId
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class __$CustomAppSecretCopyWithImpl<$Res>
 | 
			
		||||
    implements _$CustomAppSecretCopyWith<$Res> {
 | 
			
		||||
  __$CustomAppSecretCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final _CustomAppSecret _self;
 | 
			
		||||
  final $Res Function(_CustomAppSecret) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of CustomAppSecret
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? secret = null,Object? description = freezed,Object? expiredAt = freezed,Object? isOidc = null,Object? appId = null,}) {
 | 
			
		||||
  return _then(_CustomAppSecret(
 | 
			
		||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,secret: null == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,isOidc: null == isOidc ? _self.isOidc : isOidc // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as bool,appId: null == appId ? _self.appId : appId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dart format on
 | 
			
		||||
							
								
								
									
										137
									
								
								lib/models/custom_app.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								lib/models/custom_app.g.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
			
		||||
 | 
			
		||||
part of 'custom_app.dart';
 | 
			
		||||
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
// JsonSerializableGenerator
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
 | 
			
		||||
_CustomApp _$CustomAppFromJson(Map<String, dynamic> json) => _CustomApp(
 | 
			
		||||
  id: json['id'] as String? ?? '',
 | 
			
		||||
  slug: json['slug'] as String? ?? '',
 | 
			
		||||
  name: json['name'] as String? ?? '',
 | 
			
		||||
  description: json['description'] as String?,
 | 
			
		||||
  status: (json['status'] as num?)?.toInt() ?? 0,
 | 
			
		||||
  picture:
 | 
			
		||||
      json['picture'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : SnCloudFile.fromJson(json['picture'] as Map<String, dynamic>),
 | 
			
		||||
  background:
 | 
			
		||||
      json['background'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : SnCloudFile.fromJson(json['background'] as Map<String, dynamic>),
 | 
			
		||||
  verification:
 | 
			
		||||
      json['verification'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : SnVerificationMark.fromJson(
 | 
			
		||||
            json['verification'] as Map<String, dynamic>,
 | 
			
		||||
          ),
 | 
			
		||||
  oauthConfig:
 | 
			
		||||
      json['oauth_config'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : CustomAppOauthConfig.fromJson(
 | 
			
		||||
            json['oauth_config'] as Map<String, dynamic>,
 | 
			
		||||
          ),
 | 
			
		||||
  links:
 | 
			
		||||
      json['links'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : CustomAppLinks.fromJson(json['links'] as Map<String, dynamic>),
 | 
			
		||||
  secrets:
 | 
			
		||||
      (json['secrets'] as List<dynamic>?)
 | 
			
		||||
          ?.map((e) => CustomAppSecret.fromJson(e as Map<String, dynamic>))
 | 
			
		||||
          .toList() ??
 | 
			
		||||
      const [],
 | 
			
		||||
  publisherId: json['publisher_id'] as String? ?? '',
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
Map<String, dynamic> _$CustomAppToJson(_CustomApp instance) =>
 | 
			
		||||
    <String, dynamic>{
 | 
			
		||||
      'id': instance.id,
 | 
			
		||||
      'slug': instance.slug,
 | 
			
		||||
      'name': instance.name,
 | 
			
		||||
      'description': instance.description,
 | 
			
		||||
      'status': instance.status,
 | 
			
		||||
      'picture': instance.picture?.toJson(),
 | 
			
		||||
      'background': instance.background?.toJson(),
 | 
			
		||||
      'verification': instance.verification?.toJson(),
 | 
			
		||||
      'oauth_config': instance.oauthConfig?.toJson(),
 | 
			
		||||
      'links': instance.links?.toJson(),
 | 
			
		||||
      'secrets': instance.secrets.map((e) => e.toJson()).toList(),
 | 
			
		||||
      'publisher_id': instance.publisherId,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
_CustomAppLinks _$CustomAppLinksFromJson(Map<String, dynamic> json) =>
 | 
			
		||||
    _CustomAppLinks(
 | 
			
		||||
      homePage: json['home_page'] as String?,
 | 
			
		||||
      privacyPolicy: json['privacy_policy'] as String?,
 | 
			
		||||
      termsOfService: json['terms_of_service'] as String?,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
Map<String, dynamic> _$CustomAppLinksToJson(_CustomAppLinks instance) =>
 | 
			
		||||
    <String, dynamic>{
 | 
			
		||||
      'home_page': instance.homePage,
 | 
			
		||||
      'privacy_policy': instance.privacyPolicy,
 | 
			
		||||
      'terms_of_service': instance.termsOfService,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
_CustomAppOauthConfig _$CustomAppOauthConfigFromJson(
 | 
			
		||||
  Map<String, dynamic> json,
 | 
			
		||||
) => _CustomAppOauthConfig(
 | 
			
		||||
  clientUri: json['client_uri'] as String?,
 | 
			
		||||
  redirectUris:
 | 
			
		||||
      (json['redirect_uris'] as List<dynamic>?)
 | 
			
		||||
          ?.map((e) => e as String)
 | 
			
		||||
          .toList() ??
 | 
			
		||||
      const [],
 | 
			
		||||
  postLogoutRedirectUris:
 | 
			
		||||
      (json['post_logout_redirect_uris'] as List<dynamic>?)
 | 
			
		||||
          ?.map((e) => e as String)
 | 
			
		||||
          .toList(),
 | 
			
		||||
  allowedScopes:
 | 
			
		||||
      (json['allowed_scopes'] as List<dynamic>?)
 | 
			
		||||
          ?.map((e) => e as String)
 | 
			
		||||
          .toList() ??
 | 
			
		||||
      const ['openid', 'profile', 'email'],
 | 
			
		||||
  allowedGrantTypes:
 | 
			
		||||
      (json['allowed_grant_types'] as List<dynamic>?)
 | 
			
		||||
          ?.map((e) => e as String)
 | 
			
		||||
          .toList() ??
 | 
			
		||||
      const ['authorization_code', 'refresh_token'],
 | 
			
		||||
  requirePkce: json['require_pkce'] as bool? ?? true,
 | 
			
		||||
  allowOfflineAccess: json['allow_offline_access'] as bool? ?? false,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
Map<String, dynamic> _$CustomAppOauthConfigToJson(
 | 
			
		||||
  _CustomAppOauthConfig instance,
 | 
			
		||||
) => <String, dynamic>{
 | 
			
		||||
  'client_uri': instance.clientUri,
 | 
			
		||||
  'redirect_uris': instance.redirectUris,
 | 
			
		||||
  'post_logout_redirect_uris': instance.postLogoutRedirectUris,
 | 
			
		||||
  'allowed_scopes': instance.allowedScopes,
 | 
			
		||||
  'allowed_grant_types': instance.allowedGrantTypes,
 | 
			
		||||
  'require_pkce': instance.requirePkce,
 | 
			
		||||
  'allow_offline_access': instance.allowOfflineAccess,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
_CustomAppSecret _$CustomAppSecretFromJson(Map<String, dynamic> json) =>
 | 
			
		||||
    _CustomAppSecret(
 | 
			
		||||
      id: json['id'] as String? ?? '',
 | 
			
		||||
      secret: json['secret'] as String? ?? '',
 | 
			
		||||
      description: json['description'] as String?,
 | 
			
		||||
      expiredAt:
 | 
			
		||||
          json['expired_at'] == null
 | 
			
		||||
              ? null
 | 
			
		||||
              : DateTime.parse(json['expired_at'] as String),
 | 
			
		||||
      isOidc: json['is_oidc'] as bool? ?? false,
 | 
			
		||||
      appId: json['app_id'] as String? ?? '',
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
Map<String, dynamic> _$CustomAppSecretToJson(_CustomAppSecret instance) =>
 | 
			
		||||
    <String, dynamic>{
 | 
			
		||||
      'id': instance.id,
 | 
			
		||||
      'secret': instance.secret,
 | 
			
		||||
      'description': instance.description,
 | 
			
		||||
      'expired_at': instance.expiredAt?.toIso8601String(),
 | 
			
		||||
      'is_oidc': instance.isOidc,
 | 
			
		||||
      'app_id': instance.appId,
 | 
			
		||||
    };
 | 
			
		||||
							
								
								
									
										14
									
								
								lib/models/developer.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								lib/models/developer.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
import 'package:freezed_annotation/freezed_annotation.dart';
 | 
			
		||||
 | 
			
		||||
part 'developer.freezed.dart';
 | 
			
		||||
part 'developer.g.dart';
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
sealed class DeveloperStats with _$DeveloperStats {
 | 
			
		||||
  const factory DeveloperStats({
 | 
			
		||||
    @Default(0) int totalCustomApps,
 | 
			
		||||
  }) = _DeveloperStats;
 | 
			
		||||
 | 
			
		||||
  factory DeveloperStats.fromJson(Map<String, dynamic> json) =>
 | 
			
		||||
      _$DeveloperStatsFromJson(json);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										148
									
								
								lib/models/developer.freezed.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								lib/models/developer.freezed.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,148 @@
 | 
			
		||||
// dart format width=80
 | 
			
		||||
// coverage:ignore-file
 | 
			
		||||
// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
			
		||||
// 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 'developer.dart';
 | 
			
		||||
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
// FreezedGenerator
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
 | 
			
		||||
// dart format off
 | 
			
		||||
T _$identity<T>(T value) => value;
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
mixin _$DeveloperStats {
 | 
			
		||||
 | 
			
		||||
 int get totalCustomApps;
 | 
			
		||||
/// Create a copy of DeveloperStats
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$DeveloperStatsCopyWith<DeveloperStats> get copyWith => _$DeveloperStatsCopyWithImpl<DeveloperStats>(this as DeveloperStats, _$identity);
 | 
			
		||||
 | 
			
		||||
  /// Serializes this DeveloperStats to a JSON map.
 | 
			
		||||
  Map<String, dynamic> toJson();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is DeveloperStats&&(identical(other.totalCustomApps, totalCustomApps) || other.totalCustomApps == totalCustomApps));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,totalCustomApps);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'DeveloperStats(totalCustomApps: $totalCustomApps)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class $DeveloperStatsCopyWith<$Res>  {
 | 
			
		||||
  factory $DeveloperStatsCopyWith(DeveloperStats value, $Res Function(DeveloperStats) _then) = _$DeveloperStatsCopyWithImpl;
 | 
			
		||||
@useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 int totalCustomApps
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class _$DeveloperStatsCopyWithImpl<$Res>
 | 
			
		||||
    implements $DeveloperStatsCopyWith<$Res> {
 | 
			
		||||
  _$DeveloperStatsCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final DeveloperStats _self;
 | 
			
		||||
  final $Res Function(DeveloperStats) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of DeveloperStats
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@pragma('vm:prefer-inline') @override $Res call({Object? totalCustomApps = null,}) {
 | 
			
		||||
  return _then(_self.copyWith(
 | 
			
		||||
totalCustomApps: null == totalCustomApps ? _self.totalCustomApps : totalCustomApps // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as int,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
@JsonSerializable()
 | 
			
		||||
 | 
			
		||||
class _DeveloperStats implements DeveloperStats {
 | 
			
		||||
  const _DeveloperStats({this.totalCustomApps = 0});
 | 
			
		||||
  factory _DeveloperStats.fromJson(Map<String, dynamic> json) => _$DeveloperStatsFromJson(json);
 | 
			
		||||
 | 
			
		||||
@override@JsonKey() final  int totalCustomApps;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of DeveloperStats
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
_$DeveloperStatsCopyWith<_DeveloperStats> get copyWith => __$DeveloperStatsCopyWithImpl<_DeveloperStats>(this, _$identity);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
Map<String, dynamic> toJson() {
 | 
			
		||||
  return _$DeveloperStatsToJson(this, );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is _DeveloperStats&&(identical(other.totalCustomApps, totalCustomApps) || other.totalCustomApps == totalCustomApps));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,totalCustomApps);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'DeveloperStats(totalCustomApps: $totalCustomApps)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class _$DeveloperStatsCopyWith<$Res> implements $DeveloperStatsCopyWith<$Res> {
 | 
			
		||||
  factory _$DeveloperStatsCopyWith(_DeveloperStats value, $Res Function(_DeveloperStats) _then) = __$DeveloperStatsCopyWithImpl;
 | 
			
		||||
@override @useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 int totalCustomApps
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class __$DeveloperStatsCopyWithImpl<$Res>
 | 
			
		||||
    implements _$DeveloperStatsCopyWith<$Res> {
 | 
			
		||||
  __$DeveloperStatsCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final _DeveloperStats _self;
 | 
			
		||||
  final $Res Function(_DeveloperStats) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of DeveloperStats
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @pragma('vm:prefer-inline') $Res call({Object? totalCustomApps = null,}) {
 | 
			
		||||
  return _then(_DeveloperStats(
 | 
			
		||||
totalCustomApps: null == totalCustomApps ? _self.totalCustomApps : totalCustomApps // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as int,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dart format on
 | 
			
		||||
							
								
								
									
										15
									
								
								lib/models/developer.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								lib/models/developer.g.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
			
		||||
 | 
			
		||||
part of 'developer.dart';
 | 
			
		||||
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
// JsonSerializableGenerator
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
 | 
			
		||||
_DeveloperStats _$DeveloperStatsFromJson(Map<String, dynamic> json) =>
 | 
			
		||||
    _DeveloperStats(
 | 
			
		||||
      totalCustomApps: (json['total_custom_apps'] as num?)?.toInt() ?? 0,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
Map<String, dynamic> _$DeveloperStatsToJson(_DeveloperStats instance) =>
 | 
			
		||||
    <String, dynamic>{'total_custom_apps': instance.totalCustomApps};
 | 
			
		||||
@@ -2,7 +2,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
 | 
			
		||||
import 'package:island/models/file.dart';
 | 
			
		||||
import 'package:island/models/post_category.dart';
 | 
			
		||||
import 'package:island/models/post_tag.dart';
 | 
			
		||||
import 'package:island/models/user.dart';
 | 
			
		||||
import 'package:island/models/publisher.dart';
 | 
			
		||||
 | 
			
		||||
part 'post.freezed.dart';
 | 
			
		||||
part 'post.g.dart';
 | 
			
		||||
@@ -32,7 +32,7 @@ sealed class SnPost with _$SnPost {
 | 
			
		||||
    String? forwardedPostId,
 | 
			
		||||
    SnPost? forwardedPost,
 | 
			
		||||
    @Default([]) List<SnCloudFile> attachments,
 | 
			
		||||
    @Default(SnPublisher()) SnPublisher publisher,
 | 
			
		||||
    required SnPublisher publisher,
 | 
			
		||||
    @Default({}) Map<String, int> reactionsCount,
 | 
			
		||||
    @Default([]) List<dynamic> reactions,
 | 
			
		||||
    @Default([]) List<PostTag> tags,
 | 
			
		||||
@@ -47,29 +47,6 @@ sealed class SnPost with _$SnPost {
 | 
			
		||||
  factory SnPost.fromJson(Map<String, dynamic> json) => _$SnPostFromJson(json);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
sealed class SnPublisher with _$SnPublisher {
 | 
			
		||||
  const factory SnPublisher({
 | 
			
		||||
    @Default('') String id,
 | 
			
		||||
    @Default(0) int type,
 | 
			
		||||
    @Default('') String name,
 | 
			
		||||
    @Default('') String nick,
 | 
			
		||||
    @Default('') String bio,
 | 
			
		||||
    SnCloudFile? picture,
 | 
			
		||||
    SnCloudFile? background,
 | 
			
		||||
    SnAccount? account,
 | 
			
		||||
    String? accountId,
 | 
			
		||||
    @Default(null) DateTime? createdAt,
 | 
			
		||||
    @Default(null) DateTime? updatedAt,
 | 
			
		||||
    DateTime? deletedAt,
 | 
			
		||||
    String? realmId,
 | 
			
		||||
    SnVerificationMark? verification,
 | 
			
		||||
  }) = _SnPublisher;
 | 
			
		||||
 | 
			
		||||
  factory SnPublisher.fromJson(Map<String, dynamic> json) =>
 | 
			
		||||
      _$SnPublisherFromJson(json);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
sealed class SnPublisherStats with _$SnPublisherStats {
 | 
			
		||||
  const factory SnPublisherStats({
 | 
			
		||||
 
 | 
			
		||||
@@ -156,7 +156,7 @@ $SnPublisherCopyWith<$Res> get publisher {
 | 
			
		||||
@JsonSerializable()
 | 
			
		||||
 | 
			
		||||
class _SnPost implements SnPost {
 | 
			
		||||
  const _SnPost({required this.id, this.title, this.description, this.language, this.editedAt, this.publishedAt = null, this.visibility = 0, this.content, this.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 [], this.publisher = const SnPublisher(), 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  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;
 | 
			
		||||
  factory _SnPost.fromJson(Map<String, dynamic> json) => _$SnPostFromJson(json);
 | 
			
		||||
 | 
			
		||||
@override final  String id;
 | 
			
		||||
@@ -195,7 +195,7 @@ class _SnPost implements SnPost {
 | 
			
		||||
  return EqualUnmodifiableListView(_attachments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@override@JsonKey() final  SnPublisher publisher;
 | 
			
		||||
@override final  SnPublisher publisher;
 | 
			
		||||
 final  Map<String, int> _reactionsCount;
 | 
			
		||||
@override@JsonKey() Map<String, int> get reactionsCount {
 | 
			
		||||
  if (_reactionsCount is EqualUnmodifiableMapView) return _reactionsCount;
 | 
			
		||||
@@ -373,274 +373,6 @@ $SnPublisherCopyWith<$Res> get publisher {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
mixin _$SnPublisher {
 | 
			
		||||
 | 
			
		||||
 String get id; int get type; String get name; String get nick; String get bio; SnCloudFile? get picture; SnCloudFile? get background; SnAccount? get account; String? get accountId; DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; String? get realmId; SnVerificationMark? get verification;
 | 
			
		||||
/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnPublisherCopyWith<SnPublisher> get copyWith => _$SnPublisherCopyWithImpl<SnPublisher>(this as SnPublisher, _$identity);
 | 
			
		||||
 | 
			
		||||
  /// Serializes this SnPublisher to a JSON map.
 | 
			
		||||
  Map<String, dynamic> toJson();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPublisher&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.account, account) || other.account == account)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.verification, verification) || other.verification == verification));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,id,type,name,nick,bio,picture,background,account,accountId,createdAt,updatedAt,deletedAt,realmId,verification);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'SnPublisher(id: $id, type: $type, name: $name, nick: $nick, bio: $bio, picture: $picture, background: $background, account: $account, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, realmId: $realmId, verification: $verification)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class $SnPublisherCopyWith<$Res>  {
 | 
			
		||||
  factory $SnPublisherCopyWith(SnPublisher value, $Res Function(SnPublisher) _then) = _$SnPublisherCopyWithImpl;
 | 
			
		||||
@useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String id, int type, String name, String nick, String bio, SnCloudFile? picture, SnCloudFile? background, SnAccount? account, String? accountId, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String? realmId, SnVerificationMark? verification
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
$SnCloudFileCopyWith<$Res>? get picture;$SnCloudFileCopyWith<$Res>? get background;$SnAccountCopyWith<$Res>? get account;$SnVerificationMarkCopyWith<$Res>? get verification;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class _$SnPublisherCopyWithImpl<$Res>
 | 
			
		||||
    implements $SnPublisherCopyWith<$Res> {
 | 
			
		||||
  _$SnPublisherCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final SnPublisher _self;
 | 
			
		||||
  final $Res Function(SnPublisher) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? type = null,Object? name = null,Object? nick = null,Object? bio = null,Object? picture = freezed,Object? background = freezed,Object? account = freezed,Object? accountId = freezed,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? realmId = freezed,Object? verification = freezed,}) {
 | 
			
		||||
  return _then(_self.copyWith(
 | 
			
		||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as int,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,nick: null == nick ? _self.nick : nick // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,bio: null == bio ? _self.bio : bio // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnCloudFile?,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnAccount?,accountId: freezed == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,realmId: freezed == realmId ? _self.realmId : realmId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnVerificationMark?,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnCloudFileCopyWith<$Res>? get picture {
 | 
			
		||||
    if (_self.picture == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnCloudFileCopyWith<$Res>(_self.picture!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(picture: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnCloudFileCopyWith<$Res>? get background {
 | 
			
		||||
    if (_self.background == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnCloudFileCopyWith<$Res>(_self.background!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(background: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnAccountCopyWith<$Res>? get account {
 | 
			
		||||
    if (_self.account == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnAccountCopyWith<$Res>(_self.account!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(account: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnVerificationMarkCopyWith<$Res>? get verification {
 | 
			
		||||
    if (_self.verification == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(verification: value));
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
@JsonSerializable()
 | 
			
		||||
 | 
			
		||||
class _SnPublisher implements SnPublisher {
 | 
			
		||||
  const _SnPublisher({this.id = '', this.type = 0, this.name = '', this.nick = '', this.bio = '', this.picture, this.background, this.account, this.accountId, this.createdAt = null, this.updatedAt = null, this.deletedAt, this.realmId, this.verification});
 | 
			
		||||
  factory _SnPublisher.fromJson(Map<String, dynamic> json) => _$SnPublisherFromJson(json);
 | 
			
		||||
 | 
			
		||||
@override@JsonKey() final  String id;
 | 
			
		||||
@override@JsonKey() final  int type;
 | 
			
		||||
@override@JsonKey() final  String name;
 | 
			
		||||
@override@JsonKey() final  String nick;
 | 
			
		||||
@override@JsonKey() final  String bio;
 | 
			
		||||
@override final  SnCloudFile? picture;
 | 
			
		||||
@override final  SnCloudFile? background;
 | 
			
		||||
@override final  SnAccount? account;
 | 
			
		||||
@override final  String? accountId;
 | 
			
		||||
@override@JsonKey() final  DateTime? createdAt;
 | 
			
		||||
@override@JsonKey() final  DateTime? updatedAt;
 | 
			
		||||
@override final  DateTime? deletedAt;
 | 
			
		||||
@override final  String? realmId;
 | 
			
		||||
@override final  SnVerificationMark? verification;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
_$SnPublisherCopyWith<_SnPublisher> get copyWith => __$SnPublisherCopyWithImpl<_SnPublisher>(this, _$identity);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
Map<String, dynamic> toJson() {
 | 
			
		||||
  return _$SnPublisherToJson(this, );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPublisher&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.account, account) || other.account == account)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.verification, verification) || other.verification == verification));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,id,type,name,nick,bio,picture,background,account,accountId,createdAt,updatedAt,deletedAt,realmId,verification);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'SnPublisher(id: $id, type: $type, name: $name, nick: $nick, bio: $bio, picture: $picture, background: $background, account: $account, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, realmId: $realmId, verification: $verification)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class _$SnPublisherCopyWith<$Res> implements $SnPublisherCopyWith<$Res> {
 | 
			
		||||
  factory _$SnPublisherCopyWith(_SnPublisher value, $Res Function(_SnPublisher) _then) = __$SnPublisherCopyWithImpl;
 | 
			
		||||
@override @useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String id, int type, String name, String nick, String bio, SnCloudFile? picture, SnCloudFile? background, SnAccount? account, String? accountId, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String? realmId, SnVerificationMark? verification
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@override $SnCloudFileCopyWith<$Res>? get picture;@override $SnCloudFileCopyWith<$Res>? get background;@override $SnAccountCopyWith<$Res>? get account;@override $SnVerificationMarkCopyWith<$Res>? get verification;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class __$SnPublisherCopyWithImpl<$Res>
 | 
			
		||||
    implements _$SnPublisherCopyWith<$Res> {
 | 
			
		||||
  __$SnPublisherCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final _SnPublisher _self;
 | 
			
		||||
  final $Res Function(_SnPublisher) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? type = null,Object? name = null,Object? nick = null,Object? bio = null,Object? picture = freezed,Object? background = freezed,Object? account = freezed,Object? accountId = freezed,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? realmId = freezed,Object? verification = freezed,}) {
 | 
			
		||||
  return _then(_SnPublisher(
 | 
			
		||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as int,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,nick: null == nick ? _self.nick : nick // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,bio: null == bio ? _self.bio : bio // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnCloudFile?,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnAccount?,accountId: freezed == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,realmId: freezed == realmId ? _self.realmId : realmId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnVerificationMark?,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnCloudFileCopyWith<$Res>? get picture {
 | 
			
		||||
    if (_self.picture == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnCloudFileCopyWith<$Res>(_self.picture!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(picture: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnCloudFileCopyWith<$Res>? get background {
 | 
			
		||||
    if (_self.background == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnCloudFileCopyWith<$Res>(_self.background!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(background: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnAccountCopyWith<$Res>? get account {
 | 
			
		||||
    if (_self.account == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnAccountCopyWith<$Res>(_self.account!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(account: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnVerificationMarkCopyWith<$Res>? get verification {
 | 
			
		||||
    if (_self.verification == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(verification: value));
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
mixin _$SnPublisherStats {
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -48,10 +48,7 @@ _SnPost _$SnPostFromJson(Map<String, dynamic> json) => _SnPost(
 | 
			
		||||
          ?.map((e) => SnCloudFile.fromJson(e as Map<String, dynamic>))
 | 
			
		||||
          .toList() ??
 | 
			
		||||
      const [],
 | 
			
		||||
  publisher:
 | 
			
		||||
      json['publisher'] == null
 | 
			
		||||
          ? const SnPublisher()
 | 
			
		||||
          : SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
 | 
			
		||||
  publisher: SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
 | 
			
		||||
  reactionsCount:
 | 
			
		||||
      (json['reactions_count'] as Map<String, dynamic>?)?.map(
 | 
			
		||||
        (k, e) => MapEntry(k, (e as num).toInt()),
 | 
			
		||||
@@ -119,64 +116,6 @@ Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
 | 
			
		||||
  'is_truncated': instance.isTruncated,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
_SnPublisher _$SnPublisherFromJson(Map<String, dynamic> json) => _SnPublisher(
 | 
			
		||||
  id: json['id'] as String? ?? '',
 | 
			
		||||
  type: (json['type'] as num?)?.toInt() ?? 0,
 | 
			
		||||
  name: json['name'] as String? ?? '',
 | 
			
		||||
  nick: json['nick'] as String? ?? '',
 | 
			
		||||
  bio: json['bio'] as String? ?? '',
 | 
			
		||||
  picture:
 | 
			
		||||
      json['picture'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : SnCloudFile.fromJson(json['picture'] as Map<String, dynamic>),
 | 
			
		||||
  background:
 | 
			
		||||
      json['background'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : SnCloudFile.fromJson(json['background'] as Map<String, dynamic>),
 | 
			
		||||
  account:
 | 
			
		||||
      json['account'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : SnAccount.fromJson(json['account'] as Map<String, dynamic>),
 | 
			
		||||
  accountId: json['account_id'] as String?,
 | 
			
		||||
  createdAt:
 | 
			
		||||
      json['created_at'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : DateTime.parse(json['created_at'] as String),
 | 
			
		||||
  updatedAt:
 | 
			
		||||
      json['updated_at'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : DateTime.parse(json['updated_at'] as String),
 | 
			
		||||
  deletedAt:
 | 
			
		||||
      json['deleted_at'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : DateTime.parse(json['deleted_at'] as String),
 | 
			
		||||
  realmId: json['realm_id'] as String?,
 | 
			
		||||
  verification:
 | 
			
		||||
      json['verification'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : SnVerificationMark.fromJson(
 | 
			
		||||
            json['verification'] as Map<String, dynamic>,
 | 
			
		||||
          ),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
Map<String, dynamic> _$SnPublisherToJson(_SnPublisher instance) =>
 | 
			
		||||
    <String, dynamic>{
 | 
			
		||||
      'id': instance.id,
 | 
			
		||||
      'type': instance.type,
 | 
			
		||||
      'name': instance.name,
 | 
			
		||||
      'nick': instance.nick,
 | 
			
		||||
      'bio': instance.bio,
 | 
			
		||||
      'picture': instance.picture?.toJson(),
 | 
			
		||||
      'background': instance.background?.toJson(),
 | 
			
		||||
      'account': instance.account?.toJson(),
 | 
			
		||||
      'account_id': instance.accountId,
 | 
			
		||||
      'created_at': instance.createdAt?.toIso8601String(),
 | 
			
		||||
      'updated_at': instance.updatedAt?.toIso8601String(),
 | 
			
		||||
      'deleted_at': instance.deletedAt?.toIso8601String(),
 | 
			
		||||
      'realm_id': instance.realmId,
 | 
			
		||||
      'verification': instance.verification?.toJson(),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
_SnPublisherStats _$SnPublisherStatsFromJson(Map<String, dynamic> json) =>
 | 
			
		||||
    _SnPublisherStats(
 | 
			
		||||
      postsCreated: (json['posts_created'] as num).toInt(),
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								lib/models/publisher.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								lib/models/publisher.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
import 'package:freezed_annotation/freezed_annotation.dart';
 | 
			
		||||
import 'package:island/models/file.dart';
 | 
			
		||||
import 'package:island/models/user.dart';
 | 
			
		||||
 | 
			
		||||
part 'publisher.freezed.dart';
 | 
			
		||||
part 'publisher.g.dart';
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
sealed class SnPublisher with _$SnPublisher {
 | 
			
		||||
  const factory SnPublisher({
 | 
			
		||||
    @Default('') String id,
 | 
			
		||||
    @Default(0) int type,
 | 
			
		||||
    @Default('') String name,
 | 
			
		||||
    @Default('') String nick,
 | 
			
		||||
    @Default('') String bio,
 | 
			
		||||
    SnCloudFile? picture,
 | 
			
		||||
    SnCloudFile? background,
 | 
			
		||||
    SnAccount? account,
 | 
			
		||||
    String? accountId,
 | 
			
		||||
    @Default(null) DateTime? createdAt,
 | 
			
		||||
    @Default(null) DateTime? updatedAt,
 | 
			
		||||
    DateTime? deletedAt,
 | 
			
		||||
    String? realmId,
 | 
			
		||||
    SnVerificationMark? verification,
 | 
			
		||||
  }) = _SnPublisher;
 | 
			
		||||
 | 
			
		||||
  factory SnPublisher.fromJson(Map<String, dynamic> json) =>
 | 
			
		||||
      _$SnPublisherFromJson(json);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
sealed class SnPublisherMember with _$SnPublisherMember {
 | 
			
		||||
  const factory SnPublisherMember({
 | 
			
		||||
    required String publisherId,
 | 
			
		||||
    required SnPublisher? publisher,
 | 
			
		||||
    required String accountId,
 | 
			
		||||
    required SnAccount? account,
 | 
			
		||||
    required int role,
 | 
			
		||||
    required DateTime? joinedAt,
 | 
			
		||||
    required DateTime createdAt,
 | 
			
		||||
    required DateTime updatedAt,
 | 
			
		||||
    required DateTime? deletedAt,
 | 
			
		||||
  }) = _SnPublisherMember;
 | 
			
		||||
 | 
			
		||||
  factory SnPublisherMember.fromJson(Map<String, dynamic> json) =>
 | 
			
		||||
      _$SnPublisherMemberFromJson(json);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										488
									
								
								lib/models/publisher.freezed.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										488
									
								
								lib/models/publisher.freezed.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,488 @@
 | 
			
		||||
// dart format width=80
 | 
			
		||||
// coverage:ignore-file
 | 
			
		||||
// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
			
		||||
// 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 'publisher.dart';
 | 
			
		||||
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
// FreezedGenerator
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
 | 
			
		||||
// dart format off
 | 
			
		||||
T _$identity<T>(T value) => value;
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
mixin _$SnPublisher {
 | 
			
		||||
 | 
			
		||||
 String get id; int get type; String get name; String get nick; String get bio; SnCloudFile? get picture; SnCloudFile? get background; SnAccount? get account; String? get accountId; DateTime? get createdAt; DateTime? get updatedAt; DateTime? get deletedAt; String? get realmId; SnVerificationMark? get verification;
 | 
			
		||||
/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnPublisherCopyWith<SnPublisher> get copyWith => _$SnPublisherCopyWithImpl<SnPublisher>(this as SnPublisher, _$identity);
 | 
			
		||||
 | 
			
		||||
  /// Serializes this SnPublisher to a JSON map.
 | 
			
		||||
  Map<String, dynamic> toJson();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPublisher&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.account, account) || other.account == account)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.verification, verification) || other.verification == verification));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,id,type,name,nick,bio,picture,background,account,accountId,createdAt,updatedAt,deletedAt,realmId,verification);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'SnPublisher(id: $id, type: $type, name: $name, nick: $nick, bio: $bio, picture: $picture, background: $background, account: $account, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, realmId: $realmId, verification: $verification)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class $SnPublisherCopyWith<$Res>  {
 | 
			
		||||
  factory $SnPublisherCopyWith(SnPublisher value, $Res Function(SnPublisher) _then) = _$SnPublisherCopyWithImpl;
 | 
			
		||||
@useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String id, int type, String name, String nick, String bio, SnCloudFile? picture, SnCloudFile? background, SnAccount? account, String? accountId, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String? realmId, SnVerificationMark? verification
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
$SnCloudFileCopyWith<$Res>? get picture;$SnCloudFileCopyWith<$Res>? get background;$SnAccountCopyWith<$Res>? get account;$SnVerificationMarkCopyWith<$Res>? get verification;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class _$SnPublisherCopyWithImpl<$Res>
 | 
			
		||||
    implements $SnPublisherCopyWith<$Res> {
 | 
			
		||||
  _$SnPublisherCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final SnPublisher _self;
 | 
			
		||||
  final $Res Function(SnPublisher) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? type = null,Object? name = null,Object? nick = null,Object? bio = null,Object? picture = freezed,Object? background = freezed,Object? account = freezed,Object? accountId = freezed,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? realmId = freezed,Object? verification = freezed,}) {
 | 
			
		||||
  return _then(_self.copyWith(
 | 
			
		||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as int,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,nick: null == nick ? _self.nick : nick // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,bio: null == bio ? _self.bio : bio // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnCloudFile?,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnAccount?,accountId: freezed == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,realmId: freezed == realmId ? _self.realmId : realmId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnVerificationMark?,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnCloudFileCopyWith<$Res>? get picture {
 | 
			
		||||
    if (_self.picture == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnCloudFileCopyWith<$Res>(_self.picture!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(picture: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnCloudFileCopyWith<$Res>? get background {
 | 
			
		||||
    if (_self.background == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnCloudFileCopyWith<$Res>(_self.background!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(background: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnAccountCopyWith<$Res>? get account {
 | 
			
		||||
    if (_self.account == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnAccountCopyWith<$Res>(_self.account!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(account: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnVerificationMarkCopyWith<$Res>? get verification {
 | 
			
		||||
    if (_self.verification == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(verification: value));
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
@JsonSerializable()
 | 
			
		||||
 | 
			
		||||
class _SnPublisher implements SnPublisher {
 | 
			
		||||
  const _SnPublisher({this.id = '', this.type = 0, this.name = '', this.nick = '', this.bio = '', this.picture, this.background, this.account, this.accountId, this.createdAt = null, this.updatedAt = null, this.deletedAt, this.realmId, this.verification});
 | 
			
		||||
  factory _SnPublisher.fromJson(Map<String, dynamic> json) => _$SnPublisherFromJson(json);
 | 
			
		||||
 | 
			
		||||
@override@JsonKey() final  String id;
 | 
			
		||||
@override@JsonKey() final  int type;
 | 
			
		||||
@override@JsonKey() final  String name;
 | 
			
		||||
@override@JsonKey() final  String nick;
 | 
			
		||||
@override@JsonKey() final  String bio;
 | 
			
		||||
@override final  SnCloudFile? picture;
 | 
			
		||||
@override final  SnCloudFile? background;
 | 
			
		||||
@override final  SnAccount? account;
 | 
			
		||||
@override final  String? accountId;
 | 
			
		||||
@override@JsonKey() final  DateTime? createdAt;
 | 
			
		||||
@override@JsonKey() final  DateTime? updatedAt;
 | 
			
		||||
@override final  DateTime? deletedAt;
 | 
			
		||||
@override final  String? realmId;
 | 
			
		||||
@override final  SnVerificationMark? verification;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
_$SnPublisherCopyWith<_SnPublisher> get copyWith => __$SnPublisherCopyWithImpl<_SnPublisher>(this, _$identity);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
Map<String, dynamic> toJson() {
 | 
			
		||||
  return _$SnPublisherToJson(this, );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPublisher&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.account, account) || other.account == account)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.verification, verification) || other.verification == verification));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,id,type,name,nick,bio,picture,background,account,accountId,createdAt,updatedAt,deletedAt,realmId,verification);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'SnPublisher(id: $id, type: $type, name: $name, nick: $nick, bio: $bio, picture: $picture, background: $background, account: $account, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, realmId: $realmId, verification: $verification)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class _$SnPublisherCopyWith<$Res> implements $SnPublisherCopyWith<$Res> {
 | 
			
		||||
  factory _$SnPublisherCopyWith(_SnPublisher value, $Res Function(_SnPublisher) _then) = __$SnPublisherCopyWithImpl;
 | 
			
		||||
@override @useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String id, int type, String name, String nick, String bio, SnCloudFile? picture, SnCloudFile? background, SnAccount? account, String? accountId, DateTime? createdAt, DateTime? updatedAt, DateTime? deletedAt, String? realmId, SnVerificationMark? verification
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@override $SnCloudFileCopyWith<$Res>? get picture;@override $SnCloudFileCopyWith<$Res>? get background;@override $SnAccountCopyWith<$Res>? get account;@override $SnVerificationMarkCopyWith<$Res>? get verification;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class __$SnPublisherCopyWithImpl<$Res>
 | 
			
		||||
    implements _$SnPublisherCopyWith<$Res> {
 | 
			
		||||
  __$SnPublisherCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final _SnPublisher _self;
 | 
			
		||||
  final $Res Function(_SnPublisher) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? type = null,Object? name = null,Object? nick = null,Object? bio = null,Object? picture = freezed,Object? background = freezed,Object? account = freezed,Object? accountId = freezed,Object? createdAt = freezed,Object? updatedAt = freezed,Object? deletedAt = freezed,Object? realmId = freezed,Object? verification = freezed,}) {
 | 
			
		||||
  return _then(_SnPublisher(
 | 
			
		||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as int,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,nick: null == nick ? _self.nick : nick // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,bio: null == bio ? _self.bio : bio // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnCloudFile?,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnAccount?,accountId: freezed == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,realmId: freezed == realmId ? _self.realmId : realmId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnVerificationMark?,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnCloudFileCopyWith<$Res>? get picture {
 | 
			
		||||
    if (_self.picture == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnCloudFileCopyWith<$Res>(_self.picture!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(picture: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnCloudFileCopyWith<$Res>? get background {
 | 
			
		||||
    if (_self.background == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnCloudFileCopyWith<$Res>(_self.background!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(background: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnAccountCopyWith<$Res>? get account {
 | 
			
		||||
    if (_self.account == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnAccountCopyWith<$Res>(_self.account!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(account: value));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of SnPublisher
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnVerificationMarkCopyWith<$Res>? get verification {
 | 
			
		||||
    if (_self.verification == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(verification: value));
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
mixin _$SnPublisherMember {
 | 
			
		||||
 | 
			
		||||
 String get publisherId; SnPublisher? get publisher; String get accountId; SnAccount? get account; int get role; DateTime? get joinedAt; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
 | 
			
		||||
/// Create a copy of SnPublisherMember
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnPublisherMemberCopyWith<SnPublisherMember> get copyWith => _$SnPublisherMemberCopyWithImpl<SnPublisherMember>(this as SnPublisherMember, _$identity);
 | 
			
		||||
 | 
			
		||||
  /// Serializes this SnPublisherMember to a JSON map.
 | 
			
		||||
  Map<String, dynamic> toJson();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPublisherMember&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.role, role) || other.role == role)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,publisherId,publisher,accountId,account,role,joinedAt,createdAt,updatedAt,deletedAt);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'SnPublisherMember(publisherId: $publisherId, publisher: $publisher, accountId: $accountId, account: $account, role: $role, joinedAt: $joinedAt, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class $SnPublisherMemberCopyWith<$Res>  {
 | 
			
		||||
  factory $SnPublisherMemberCopyWith(SnPublisherMember value, $Res Function(SnPublisherMember) _then) = _$SnPublisherMemberCopyWithImpl;
 | 
			
		||||
@useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String publisherId, SnPublisher? publisher, String accountId, SnAccount? account, int role, DateTime? joinedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
$SnPublisherCopyWith<$Res>? get publisher;$SnAccountCopyWith<$Res>? get account;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class _$SnPublisherMemberCopyWithImpl<$Res>
 | 
			
		||||
    implements $SnPublisherMemberCopyWith<$Res> {
 | 
			
		||||
  _$SnPublisherMemberCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final SnPublisherMember _self;
 | 
			
		||||
  final $Res Function(SnPublisherMember) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of SnPublisherMember
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@pragma('vm:prefer-inline') @override $Res call({Object? publisherId = null,Object? publisher = freezed,Object? accountId = null,Object? account = freezed,Object? role = null,Object? joinedAt = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
 | 
			
		||||
  return _then(_self.copyWith(
 | 
			
		||||
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?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnAccount?,role: null == role ? _self.role : role // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as int,joinedAt: freezed == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
/// Create a copy of SnPublisherMember
 | 
			
		||||
/// 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));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of SnPublisherMember
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnAccountCopyWith<$Res>? get account {
 | 
			
		||||
    if (_self.account == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnAccountCopyWith<$Res>(_self.account!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(account: value));
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
@JsonSerializable()
 | 
			
		||||
 | 
			
		||||
class _SnPublisherMember implements SnPublisherMember {
 | 
			
		||||
  const _SnPublisherMember({required this.publisherId, required this.publisher, required this.accountId, required this.account, required this.role, required this.joinedAt, required this.createdAt, required this.updatedAt, required this.deletedAt});
 | 
			
		||||
  factory _SnPublisherMember.fromJson(Map<String, dynamic> json) => _$SnPublisherMemberFromJson(json);
 | 
			
		||||
 | 
			
		||||
@override final  String publisherId;
 | 
			
		||||
@override final  SnPublisher? publisher;
 | 
			
		||||
@override final  String accountId;
 | 
			
		||||
@override final  SnAccount? account;
 | 
			
		||||
@override final  int role;
 | 
			
		||||
@override final  DateTime? joinedAt;
 | 
			
		||||
@override final  DateTime createdAt;
 | 
			
		||||
@override final  DateTime updatedAt;
 | 
			
		||||
@override final  DateTime? deletedAt;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of SnPublisherMember
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
_$SnPublisherMemberCopyWith<_SnPublisherMember> get copyWith => __$SnPublisherMemberCopyWithImpl<_SnPublisherMember>(this, _$identity);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
Map<String, dynamic> toJson() {
 | 
			
		||||
  return _$SnPublisherMemberToJson(this, );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPublisherMember&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.role, role) || other.role == role)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,publisherId,publisher,accountId,account,role,joinedAt,createdAt,updatedAt,deletedAt);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'SnPublisherMember(publisherId: $publisherId, publisher: $publisher, accountId: $accountId, account: $account, role: $role, joinedAt: $joinedAt, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class _$SnPublisherMemberCopyWith<$Res> implements $SnPublisherMemberCopyWith<$Res> {
 | 
			
		||||
  factory _$SnPublisherMemberCopyWith(_SnPublisherMember value, $Res Function(_SnPublisherMember) _then) = __$SnPublisherMemberCopyWithImpl;
 | 
			
		||||
@override @useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String publisherId, SnPublisher? publisher, String accountId, SnAccount? account, int role, DateTime? joinedAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@override $SnPublisherCopyWith<$Res>? get publisher;@override $SnAccountCopyWith<$Res>? get account;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class __$SnPublisherMemberCopyWithImpl<$Res>
 | 
			
		||||
    implements _$SnPublisherMemberCopyWith<$Res> {
 | 
			
		||||
  __$SnPublisherMemberCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final _SnPublisherMember _self;
 | 
			
		||||
  final $Res Function(_SnPublisherMember) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of SnPublisherMember
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @pragma('vm:prefer-inline') $Res call({Object? publisherId = null,Object? publisher = freezed,Object? accountId = null,Object? account = freezed,Object? role = null,Object? joinedAt = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
 | 
			
		||||
  return _then(_SnPublisherMember(
 | 
			
		||||
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?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as SnAccount?,role: null == role ? _self.role : role // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as int,joinedAt: freezed == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create a copy of SnPublisherMember
 | 
			
		||||
/// 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));
 | 
			
		||||
  });
 | 
			
		||||
}/// Create a copy of SnPublisherMember
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnAccountCopyWith<$Res>? get account {
 | 
			
		||||
    if (_self.account == null) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $SnAccountCopyWith<$Res>(_self.account!, (value) {
 | 
			
		||||
    return _then(_self.copyWith(account: value));
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dart format on
 | 
			
		||||
							
								
								
									
										103
									
								
								lib/models/publisher.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								lib/models/publisher.g.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
			
		||||
// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
			
		||||
 | 
			
		||||
part of 'publisher.dart';
 | 
			
		||||
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
// JsonSerializableGenerator
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
 | 
			
		||||
_SnPublisher _$SnPublisherFromJson(Map<String, dynamic> json) => _SnPublisher(
 | 
			
		||||
  id: json['id'] as String? ?? '',
 | 
			
		||||
  type: (json['type'] as num?)?.toInt() ?? 0,
 | 
			
		||||
  name: json['name'] as String? ?? '',
 | 
			
		||||
  nick: json['nick'] as String? ?? '',
 | 
			
		||||
  bio: json['bio'] as String? ?? '',
 | 
			
		||||
  picture:
 | 
			
		||||
      json['picture'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : SnCloudFile.fromJson(json['picture'] as Map<String, dynamic>),
 | 
			
		||||
  background:
 | 
			
		||||
      json['background'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : SnCloudFile.fromJson(json['background'] as Map<String, dynamic>),
 | 
			
		||||
  account:
 | 
			
		||||
      json['account'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : SnAccount.fromJson(json['account'] as Map<String, dynamic>),
 | 
			
		||||
  accountId: json['account_id'] as String?,
 | 
			
		||||
  createdAt:
 | 
			
		||||
      json['created_at'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : DateTime.parse(json['created_at'] as String),
 | 
			
		||||
  updatedAt:
 | 
			
		||||
      json['updated_at'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : DateTime.parse(json['updated_at'] as String),
 | 
			
		||||
  deletedAt:
 | 
			
		||||
      json['deleted_at'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : DateTime.parse(json['deleted_at'] as String),
 | 
			
		||||
  realmId: json['realm_id'] as String?,
 | 
			
		||||
  verification:
 | 
			
		||||
      json['verification'] == null
 | 
			
		||||
          ? null
 | 
			
		||||
          : SnVerificationMark.fromJson(
 | 
			
		||||
            json['verification'] as Map<String, dynamic>,
 | 
			
		||||
          ),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
Map<String, dynamic> _$SnPublisherToJson(_SnPublisher instance) =>
 | 
			
		||||
    <String, dynamic>{
 | 
			
		||||
      'id': instance.id,
 | 
			
		||||
      'type': instance.type,
 | 
			
		||||
      'name': instance.name,
 | 
			
		||||
      'nick': instance.nick,
 | 
			
		||||
      'bio': instance.bio,
 | 
			
		||||
      'picture': instance.picture?.toJson(),
 | 
			
		||||
      'background': instance.background?.toJson(),
 | 
			
		||||
      'account': instance.account?.toJson(),
 | 
			
		||||
      'account_id': instance.accountId,
 | 
			
		||||
      'created_at': instance.createdAt?.toIso8601String(),
 | 
			
		||||
      'updated_at': instance.updatedAt?.toIso8601String(),
 | 
			
		||||
      'deleted_at': instance.deletedAt?.toIso8601String(),
 | 
			
		||||
      'realm_id': instance.realmId,
 | 
			
		||||
      'verification': instance.verification?.toJson(),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
_SnPublisherMember _$SnPublisherMemberFromJson(Map<String, dynamic> json) =>
 | 
			
		||||
    _SnPublisherMember(
 | 
			
		||||
      publisherId: json['publisher_id'] as String,
 | 
			
		||||
      publisher:
 | 
			
		||||
          json['publisher'] == null
 | 
			
		||||
              ? null
 | 
			
		||||
              : SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
 | 
			
		||||
      accountId: json['account_id'] as String,
 | 
			
		||||
      account:
 | 
			
		||||
          json['account'] == null
 | 
			
		||||
              ? null
 | 
			
		||||
              : SnAccount.fromJson(json['account'] as Map<String, dynamic>),
 | 
			
		||||
      role: (json['role'] as num).toInt(),
 | 
			
		||||
      joinedAt:
 | 
			
		||||
          json['joined_at'] == null
 | 
			
		||||
              ? null
 | 
			
		||||
              : DateTime.parse(json['joined_at'] as String),
 | 
			
		||||
      createdAt: DateTime.parse(json['created_at'] as String),
 | 
			
		||||
      updatedAt: DateTime.parse(json['updated_at'] as String),
 | 
			
		||||
      deletedAt:
 | 
			
		||||
          json['deleted_at'] == null
 | 
			
		||||
              ? null
 | 
			
		||||
              : DateTime.parse(json['deleted_at'] as String),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
Map<String, dynamic> _$SnPublisherMemberToJson(_SnPublisherMember instance) =>
 | 
			
		||||
    <String, dynamic>{
 | 
			
		||||
      'publisher_id': instance.publisherId,
 | 
			
		||||
      'publisher': instance.publisher?.toJson(),
 | 
			
		||||
      'account_id': instance.accountId,
 | 
			
		||||
      'account': instance.account?.toJson(),
 | 
			
		||||
      'role': instance.role,
 | 
			
		||||
      'joined_at': instance.joinedAt?.toIso8601String(),
 | 
			
		||||
      'created_at': instance.createdAt.toIso8601String(),
 | 
			
		||||
      'updated_at': instance.updatedAt.toIso8601String(),
 | 
			
		||||
      'deleted_at': instance.deletedAt?.toIso8601String(),
 | 
			
		||||
    };
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import 'package:freezed_annotation/freezed_annotation.dart';
 | 
			
		||||
import 'package:island/models/file.dart';
 | 
			
		||||
import 'package:island/models/post.dart';
 | 
			
		||||
import 'package:island/models/publisher.dart';
 | 
			
		||||
 | 
			
		||||
part 'sticker.freezed.dart';
 | 
			
		||||
part 'sticker.g.dart';
 | 
			
		||||
 
 | 
			
		||||
@@ -11,11 +11,6 @@ class UserInfoNotifier extends StateNotifier<AsyncValue<SnAccount?>> {
 | 
			
		||||
 | 
			
		||||
  UserInfoNotifier(this._ref) : super(const AsyncValue.data(null));
 | 
			
		||||
 | 
			
		||||
  Future<String?> getAccessToken() async {
 | 
			
		||||
    final prefs = _ref.read(sharedPreferencesProvider);
 | 
			
		||||
    return prefs.getString(kTokenPairStoreKey);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> fetchUser() async {
 | 
			
		||||
    try {
 | 
			
		||||
      final client = _ref.read(apiClientProvider);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										120
									
								
								lib/route.dart
									
									
									
									
									
								
							
							
						
						
									
										120
									
								
								lib/route.dart
									
									
									
									
									
								
							@@ -1,6 +1,10 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:go_router/go_router.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/screens/developers/apps.dart';
 | 
			
		||||
import 'package:island/screens/developers/edit_app.dart';
 | 
			
		||||
import 'package:island/screens/developers/new_app.dart';
 | 
			
		||||
import 'package:island/screens/developers/hub.dart';
 | 
			
		||||
import 'package:island/widgets/app_wrapper.dart';
 | 
			
		||||
import 'package:island/screens/tabs.dart';
 | 
			
		||||
 | 
			
		||||
@@ -79,33 +83,37 @@ final routerProvider = Provider<GoRouter>((ref) {
 | 
			
		||||
              return EventCalanderScreen(name: name);
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
          GoRoute(
 | 
			
		||||
            path: '/creators',
 | 
			
		||||
            builder: (context, state) => const CreatorHubScreen(),
 | 
			
		||||
          ShellRoute(
 | 
			
		||||
            builder:
 | 
			
		||||
                (context, state, child) => CreatorHubShellScreen(child: child),
 | 
			
		||||
            routes: [
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: ':name/posts',
 | 
			
		||||
                path: '/creators',
 | 
			
		||||
                builder: (context, state) => const CreatorHubScreen(),
 | 
			
		||||
              ),
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: '/creators/:name/posts',
 | 
			
		||||
                builder: (context, state) {
 | 
			
		||||
                  final name = state.pathParameters['name']!;
 | 
			
		||||
                  return CreatorPostListScreen(pubName: name);
 | 
			
		||||
                },
 | 
			
		||||
              ),
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: ':name/stickers',
 | 
			
		||||
                path: '/creators/:name/stickers',
 | 
			
		||||
                builder: (context, state) {
 | 
			
		||||
                  final name = state.pathParameters['name']!;
 | 
			
		||||
                  return StickersScreen(pubName: name);
 | 
			
		||||
                },
 | 
			
		||||
              ),
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: ':name/stickers/new',
 | 
			
		||||
                path: '/creators/:name/stickers/new',
 | 
			
		||||
                builder: (context, state) {
 | 
			
		||||
                  final name = state.pathParameters['name']!;
 | 
			
		||||
                  return NewStickerPacksScreen(pubName: name);
 | 
			
		||||
                },
 | 
			
		||||
              ),
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: ':name/stickers/:packId/edit',
 | 
			
		||||
                path: '/creators/:name/stickers/:packId/edit',
 | 
			
		||||
                builder: (context, state) {
 | 
			
		||||
                  final name = state.pathParameters['name']!;
 | 
			
		||||
                  final packId = state.pathParameters['packId']!;
 | 
			
		||||
@@ -113,7 +121,7 @@ final routerProvider = Provider<GoRouter>((ref) {
 | 
			
		||||
                },
 | 
			
		||||
              ),
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: ':name/stickers/:packId',
 | 
			
		||||
                path: '/creators/:name/stickers/:packId',
 | 
			
		||||
                builder: (context, state) {
 | 
			
		||||
                  final name = state.pathParameters['name']!;
 | 
			
		||||
                  final packId = state.pathParameters['packId']!;
 | 
			
		||||
@@ -121,14 +129,14 @@ final routerProvider = Provider<GoRouter>((ref) {
 | 
			
		||||
                },
 | 
			
		||||
              ),
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: ':name/stickers/:packId/new',
 | 
			
		||||
                path: '/creators/:name/stickers/:packId/new',
 | 
			
		||||
                builder: (context, state) {
 | 
			
		||||
                  final packId = state.pathParameters['packId']!;
 | 
			
		||||
                  return NewStickersScreen(packId: packId);
 | 
			
		||||
                },
 | 
			
		||||
              ),
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: ':name/stickers/:packId/:id/edit',
 | 
			
		||||
                path: '/creators/:name/stickers/:packId/:id/edit',
 | 
			
		||||
                builder: (context, state) {
 | 
			
		||||
                  final packId = state.pathParameters['packId']!;
 | 
			
		||||
                  final id = state.pathParameters['id']!;
 | 
			
		||||
@@ -136,11 +144,11 @@ final routerProvider = Provider<GoRouter>((ref) {
 | 
			
		||||
                },
 | 
			
		||||
              ),
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: 'new',
 | 
			
		||||
                path: '/creators/new',
 | 
			
		||||
                builder: (context, state) => const NewPublisherScreen(),
 | 
			
		||||
              ),
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: ':name/edit',
 | 
			
		||||
                path: '/creators/:name/edit',
 | 
			
		||||
                builder: (context, state) {
 | 
			
		||||
                  final name = state.pathParameters['name']!;
 | 
			
		||||
                  return EditPublisherScreen(name: name);
 | 
			
		||||
@@ -148,6 +156,36 @@ final routerProvider = Provider<GoRouter>((ref) {
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
          ShellRoute(
 | 
			
		||||
            builder:
 | 
			
		||||
                (context, state, child) =>
 | 
			
		||||
                    DeveloperHubShellScreen(child: child),
 | 
			
		||||
            routes: [
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: '/developers',
 | 
			
		||||
                builder: (context, state) => const DeveloperHubScreen(),
 | 
			
		||||
              ),
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: '/developers/:name/apps',
 | 
			
		||||
                builder: (context, state) => CustomAppsScreen(
 | 
			
		||||
                  publisherName: state.pathParameters['name']!,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: '/developers/:name/apps/new',
 | 
			
		||||
                builder: (context, state) => NewCustomAppScreen(
 | 
			
		||||
                  publisherName: state.pathParameters['name']!,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: '/developers/:name/apps/:id',
 | 
			
		||||
                builder: (context, state) => EditAppScreen(
 | 
			
		||||
                  publisherName: state.pathParameters['name']!,
 | 
			
		||||
                  id: state.pathParameters['id']!,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
          // Auth routes
 | 
			
		||||
          GoRoute(
 | 
			
		||||
@@ -173,56 +211,64 @@ final routerProvider = Provider<GoRouter>((ref) {
 | 
			
		||||
            },
 | 
			
		||||
            routes: [
 | 
			
		||||
              // Explore tab
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: '/',
 | 
			
		||||
                builder: (context, state) => const ExploreScreen(),
 | 
			
		||||
              ShellRoute(
 | 
			
		||||
                builder:
 | 
			
		||||
                    (context, state, child) => ExploreShellScreen(child: child),
 | 
			
		||||
                routes: [
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: 'posts/:id',
 | 
			
		||||
                    path: '/',
 | 
			
		||||
                    builder: (context, state) => const ExploreScreen(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: '/posts/:id',
 | 
			
		||||
                    builder: (context, state) {
 | 
			
		||||
                      final id = state.pathParameters['id']!;
 | 
			
		||||
                      return PostDetailScreen(id: id);
 | 
			
		||||
                    },
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: 'publishers/:name',
 | 
			
		||||
                    path: '/publishers/:name',
 | 
			
		||||
                    builder: (context, state) {
 | 
			
		||||
                      final name = state.pathParameters['name']!;
 | 
			
		||||
                      return PublisherProfileScreen(name: name);
 | 
			
		||||
                    },
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: 'discovery/realms',
 | 
			
		||||
                    path: '/discovery/realms',
 | 
			
		||||
                    builder: (context, state) => const DiscoveryRealmsScreen(),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
 | 
			
		||||
              // Chat tab
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: '/chat',
 | 
			
		||||
                builder: (context, state) => const ChatListScreen(),
 | 
			
		||||
              ShellRoute(
 | 
			
		||||
                builder:
 | 
			
		||||
                    (context, state, child) => ChatShellScreen(child: child),
 | 
			
		||||
                routes: [
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: 'new',
 | 
			
		||||
                    path: '/chat',
 | 
			
		||||
                    builder: (context, state) => const ChatListScreen(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: '/chat/new',
 | 
			
		||||
                    builder: (context, state) => const NewChatScreen(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: ':id',
 | 
			
		||||
                    path: '/chat/:id',
 | 
			
		||||
                    builder: (context, state) {
 | 
			
		||||
                      final id = state.pathParameters['id']!;
 | 
			
		||||
                      return ChatRoomScreen(id: id);
 | 
			
		||||
                    },
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: ':id/edit',
 | 
			
		||||
                    path: '/chat/:id/edit',
 | 
			
		||||
                    builder: (context, state) {
 | 
			
		||||
                      final id = state.pathParameters['id']!;
 | 
			
		||||
                      return EditChatScreen(id: id);
 | 
			
		||||
                    },
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: ':id/detail',
 | 
			
		||||
                    path: '/chat/:id/detail',
 | 
			
		||||
                    builder: (context, state) {
 | 
			
		||||
                      final id = state.pathParameters['id']!;
 | 
			
		||||
                      return ChatDetailScreen(id: id);
 | 
			
		||||
@@ -258,39 +304,43 @@ final routerProvider = Provider<GoRouter>((ref) {
 | 
			
		||||
              ),
 | 
			
		||||
 | 
			
		||||
              // Account tab
 | 
			
		||||
              GoRoute(
 | 
			
		||||
                path: '/account',
 | 
			
		||||
                builder: (context, state) => const AccountScreen(),
 | 
			
		||||
              ShellRoute(
 | 
			
		||||
                builder:
 | 
			
		||||
                    (context, state, child) => AccountShellScreen(child: child),
 | 
			
		||||
                routes: [
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: 'notifications',
 | 
			
		||||
                    path: '/account',
 | 
			
		||||
                    builder: (context, state) => const AccountScreen(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: '/account/notifications',
 | 
			
		||||
                    builder: (context, state) => const NotificationScreen(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: 'wallet',
 | 
			
		||||
                    path: '/account/wallet',
 | 
			
		||||
                    builder: (context, state) => const WalletScreen(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: 'relationships',
 | 
			
		||||
                    path: '/account/relationships',
 | 
			
		||||
                    builder: (context, state) => const RelationshipScreen(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: ':name',
 | 
			
		||||
                    path: '/account/:name',
 | 
			
		||||
                    builder: (context, state) {
 | 
			
		||||
                      final name = state.pathParameters['name']!;
 | 
			
		||||
                      return AccountProfileScreen(name: name);
 | 
			
		||||
                    },
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: 'me/update',
 | 
			
		||||
                    path: '/account/me/update',
 | 
			
		||||
                    builder: (context, state) => const UpdateProfileScreen(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: 'me/leveling',
 | 
			
		||||
                    path: '/account/me/leveling',
 | 
			
		||||
                    builder: (context, state) => const LevelingScreen(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: 'settings',
 | 
			
		||||
                    path: '/account/settings',
 | 
			
		||||
                    builder: (context, state) => const AccountSettingsScreen(),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
 
 | 
			
		||||
@@ -143,7 +143,7 @@ class AccountScreen extends HookConsumerWidget {
 | 
			
		||||
                progress: user.value!.profile.levelingProgress,
 | 
			
		||||
              ),
 | 
			
		||||
              onTap: () {
 | 
			
		||||
                context.push('/account/leveling');
 | 
			
		||||
                context.push('/account/me/leveling');
 | 
			
		||||
              },
 | 
			
		||||
            ).padding(horizontal: 12),
 | 
			
		||||
            Row(
 | 
			
		||||
@@ -178,7 +178,9 @@ class AccountScreen extends HookConsumerWidget {
 | 
			
		||||
                          Text('developerPortalDescription').tr(),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ).padding(horizontal: 16, vertical: 12),
 | 
			
		||||
                      onTap: () {},
 | 
			
		||||
                      onTap: () {
 | 
			
		||||
                        context.push('/developers');
 | 
			
		||||
                      },
 | 
			
		||||
                    ),
 | 
			
		||||
                  ).height(140),
 | 
			
		||||
                ),
 | 
			
		||||
@@ -210,7 +212,7 @@ class AccountScreen extends HookConsumerWidget {
 | 
			
		||||
              contentPadding: EdgeInsets.symmetric(horizontal: 24),
 | 
			
		||||
              title: Text('wallet').tr(),
 | 
			
		||||
              onTap: () {
 | 
			
		||||
                context.push('/wallet');
 | 
			
		||||
                context.push('/account/wallet');
 | 
			
		||||
              },
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
 
 | 
			
		||||
@@ -53,17 +53,21 @@ Future<List<SnAccountBadge>> accountBadges(Ref ref, String uname) async {
 | 
			
		||||
 | 
			
		||||
@riverpod
 | 
			
		||||
Future<Color?> accountAppbarForcegroundColor(Ref ref, String uname) async {
 | 
			
		||||
  final account = await ref.watch(accountProvider(uname).future);
 | 
			
		||||
  if (account.profile.background == null) return null;
 | 
			
		||||
  final palette = await PaletteGenerator.fromImageProvider(
 | 
			
		||||
    CloudImageWidget.provider(
 | 
			
		||||
      fileId: account.profile.background!.id,
 | 
			
		||||
      serverUrl: ref.watch(serverUrlProvider),
 | 
			
		||||
    ),
 | 
			
		||||
  );
 | 
			
		||||
  final dominantColor = palette.dominantColor?.color;
 | 
			
		||||
  if (dominantColor == null) return null;
 | 
			
		||||
  return dominantColor.computeLuminance() > 0.5 ? Colors.black : Colors.white;
 | 
			
		||||
  try {
 | 
			
		||||
    final account = await ref.watch(accountProvider(uname).future);
 | 
			
		||||
    if (account.profile.background == null) return null;
 | 
			
		||||
    final palette = await PaletteGenerator.fromImageProvider(
 | 
			
		||||
      CloudImageWidget.provider(
 | 
			
		||||
        fileId: account.profile.background!.id,
 | 
			
		||||
        serverUrl: ref.watch(serverUrlProvider),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    final dominantColor = palette.dominantColor?.color;
 | 
			
		||||
    if (dominantColor == null) return null;
 | 
			
		||||
    return dominantColor.computeLuminance() > 0.5 ? Colors.black : Colors.white;
 | 
			
		||||
  } catch (_) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@riverpod
 | 
			
		||||
 
 | 
			
		||||
@@ -268,7 +268,7 @@ class _AccountBadgesProviderElement
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String _$accountAppbarForcegroundColorHash() =>
 | 
			
		||||
    r'f654a7a5594eda1500906e9ad023c22772257a9b';
 | 
			
		||||
    r'8ee0cae10817b77fb09548a482f5247662b4374c';
 | 
			
		||||
 | 
			
		||||
/// See also [accountAppbarForcegroundColor].
 | 
			
		||||
@ProviderFor(accountAppbarForcegroundColor)
 | 
			
		||||
 
 | 
			
		||||
@@ -676,17 +676,36 @@ class EditChatScreen extends HookConsumerWidget {
 | 
			
		||||
                      (_) => FocusManager.instance.primaryFocus?.unfocus(),
 | 
			
		||||
                ),
 | 
			
		||||
                const SizedBox(height: 16),
 | 
			
		||||
                CheckboxListTile(
 | 
			
		||||
                  title: const Text('isPublic').tr(),
 | 
			
		||||
                  subtitle: const Text('isPublicHint').tr(),
 | 
			
		||||
                  value: isPublic.value,
 | 
			
		||||
                  onChanged: (value) => isPublic.value = value ?? false,
 | 
			
		||||
                ),
 | 
			
		||||
                CheckboxListTile(
 | 
			
		||||
                  title: const Text('isCommunity').tr(),
 | 
			
		||||
                  subtitle: const Text('isCommunityHint').tr(),
 | 
			
		||||
                  value: isCommunity.value,
 | 
			
		||||
                  onChanged: (value) => isCommunity.value = value ?? false,
 | 
			
		||||
                Card(
 | 
			
		||||
                  margin: EdgeInsets.zero,
 | 
			
		||||
                  child: Column(
 | 
			
		||||
                    children: [
 | 
			
		||||
                      CheckboxListTile(
 | 
			
		||||
                        secondary: const Icon(Symbols.public),
 | 
			
		||||
                        title: Text('publicChat').tr(),
 | 
			
		||||
                        subtitle: Text('publicChatDescription').tr(),
 | 
			
		||||
                        value: isPublic.value,
 | 
			
		||||
                        onChanged: (value) {
 | 
			
		||||
                          isPublic.value = value ?? true;
 | 
			
		||||
                        },
 | 
			
		||||
                        shape: RoundedRectangleBorder(
 | 
			
		||||
                          borderRadius: BorderRadius.circular(8),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                      CheckboxListTile(
 | 
			
		||||
                        secondary: const Icon(Symbols.travel_explore),
 | 
			
		||||
                        title: Text('communityChat').tr(),
 | 
			
		||||
                        subtitle: Text('communityChatDescription').tr(),
 | 
			
		||||
                        value: isCommunity.value,
 | 
			
		||||
                        onChanged: (value) {
 | 
			
		||||
                          isCommunity.value = value ?? false;
 | 
			
		||||
                        },
 | 
			
		||||
                        shape: RoundedRectangleBorder(
 | 
			
		||||
                          borderRadius: BorderRadius.circular(8),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                const SizedBox(height: 16),
 | 
			
		||||
                Align(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import 'package:dio/dio.dart';
 | 
			
		||||
import 'package:dropdown_button2/dropdown_button2.dart';
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
@@ -6,14 +7,20 @@ import 'package:flutter_hooks/flutter_hooks.dart';
 | 
			
		||||
import 'package:gap/gap.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/post.dart';
 | 
			
		||||
import 'package:island/models/publisher.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
import 'package:island/screens/creators/publishers.dart';
 | 
			
		||||
import 'package:island/services/responsive.dart';
 | 
			
		||||
import 'package:island/services/text.dart';
 | 
			
		||||
import 'package:island/widgets/account/account_picker.dart';
 | 
			
		||||
import 'package:island/widgets/alert.dart';
 | 
			
		||||
import 'package:island/widgets/app_scaffold.dart';
 | 
			
		||||
import 'package:island/widgets/content/cloud_files.dart';
 | 
			
		||||
import 'package:island/widgets/content/sheet.dart';
 | 
			
		||||
import 'package:island/widgets/response.dart';
 | 
			
		||||
import 'package:material_symbols_icons/symbols.dart';
 | 
			
		||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
			
		||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
 | 
			
		||||
import 'package:styled_widget/styled_widget.dart';
 | 
			
		||||
 | 
			
		||||
part 'hub.g.dart';
 | 
			
		||||
@@ -26,6 +33,73 @@ Future<SnPublisherStats?> publisherStats(Ref ref, String? uname) async {
 | 
			
		||||
  return SnPublisherStats.fromJson(resp.data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@riverpod
 | 
			
		||||
Future<SnPublisherMember?> publisherIdentity(Ref ref, String uname) async {
 | 
			
		||||
  try {
 | 
			
		||||
    final apiClient = ref.watch(apiClientProvider);
 | 
			
		||||
    final response = await apiClient.get('/publishers/$uname/members/me');
 | 
			
		||||
    return SnPublisherMember.fromJson(response.data);
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    if (err is DioException && err.response?.statusCode == 404) {
 | 
			
		||||
      return null; // No identity found, user is not a member
 | 
			
		||||
    }
 | 
			
		||||
    rethrow;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@riverpod
 | 
			
		||||
Future<Map<String, bool>> publisherFeatures(Ref ref, String? uname) async {
 | 
			
		||||
  if (uname == null) return {};
 | 
			
		||||
  final apiClient = ref.watch(apiClientProvider);
 | 
			
		||||
  final response = await apiClient.get('/publishers/$uname/features');
 | 
			
		||||
  return Map<String, bool>.from(response.data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@riverpod
 | 
			
		||||
Future<List<SnPublisherMember>> publisherInvites(Ref ref) async {
 | 
			
		||||
  final client = ref.watch(apiClientProvider);
 | 
			
		||||
  final resp = await client.get('/publishers/invites');
 | 
			
		||||
  return resp.data
 | 
			
		||||
      .map((e) => SnPublisherMember.fromJson(e))
 | 
			
		||||
      .cast<SnPublisherMember>()
 | 
			
		||||
      .toList();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@riverpod
 | 
			
		||||
class PublisherMemberListNotifier extends _$PublisherMemberListNotifier
 | 
			
		||||
    with CursorPagingNotifierMixin<SnPublisherMember> {
 | 
			
		||||
  static const int _pageSize = 20;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<CursorPagingData<SnPublisherMember>> build(String uname) async {
 | 
			
		||||
    return fetch();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<CursorPagingData<SnPublisherMember>> fetch({String? cursor}) async {
 | 
			
		||||
    final apiClient = ref.read(apiClientProvider);
 | 
			
		||||
    final offset = cursor != null ? int.parse(cursor) : 0;
 | 
			
		||||
 | 
			
		||||
    final response = await apiClient.get(
 | 
			
		||||
      '/publishers/$uname/members',
 | 
			
		||||
      queryParameters: {'offset': offset, 'take': _pageSize},
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    final total = int.parse(response.headers.value('X-Total') ?? '0');
 | 
			
		||||
    final List<dynamic> data = response.data;
 | 
			
		||||
    final members = data.map((e) => SnPublisherMember.fromJson(e)).toList();
 | 
			
		||||
 | 
			
		||||
    final hasMore = offset + members.length < total;
 | 
			
		||||
    final nextCursor = hasMore ? (offset + members.length).toString() : null;
 | 
			
		||||
 | 
			
		||||
    return CursorPagingData(
 | 
			
		||||
      items: members,
 | 
			
		||||
      hasMore: hasMore,
 | 
			
		||||
      nextCursor: nextCursor,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class CreatorHubShellScreen extends StatelessWidget {
 | 
			
		||||
  final Widget child;
 | 
			
		||||
  const CreatorHubShellScreen({super.key, required this.child});
 | 
			
		||||
@@ -58,21 +132,20 @@ class CreatorHubScreen extends HookConsumerWidget {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final publishers = ref.watch(publishersManagedProvider);
 | 
			
		||||
    final publisherInvites = ref.watch(publisherInvitesProvider);
 | 
			
		||||
    final currentPublisher = useState<SnPublisher?>(
 | 
			
		||||
      publishers.value?.firstOrNull,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    void updatePublisher() {
 | 
			
		||||
      context
 | 
			
		||||
          .push('/creators/${currentPublisher.value!.name}/edit')
 | 
			
		||||
          .then((value) async {
 | 
			
		||||
            if (value == null) return;
 | 
			
		||||
            final data = await ref.refresh(publishersManagedProvider.future);
 | 
			
		||||
            currentPublisher.value =
 | 
			
		||||
                data
 | 
			
		||||
                    .where((e) => e.id == currentPublisher.value!.id)
 | 
			
		||||
                    .firstOrNull;
 | 
			
		||||
          });
 | 
			
		||||
      context.push('/creators/${currentPublisher.value!.name}/edit').then((
 | 
			
		||||
        value,
 | 
			
		||||
      ) async {
 | 
			
		||||
        if (value == null) return;
 | 
			
		||||
        final data = await ref.refresh(publishersManagedProvider.future);
 | 
			
		||||
        currentPublisher.value =
 | 
			
		||||
            data.where((e) => e.id == currentPublisher.value!.id).firstOrNull;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void deletePublisher() {
 | 
			
		||||
@@ -120,12 +193,40 @@ class CreatorHubScreen extends HookConsumerWidget {
 | 
			
		||||
      publisherStatsProvider(currentPublisher.value?.name),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    final publisherFeatures = ref.watch(
 | 
			
		||||
      publisherFeaturesProvider(currentPublisher.value?.name),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      noBackground: false,
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: !isWide ? const PageBackButton() : null,
 | 
			
		||||
        title: Text('creatorHub').tr(),
 | 
			
		||||
        actions: [
 | 
			
		||||
          IconButton(
 | 
			
		||||
            icon: Badge(
 | 
			
		||||
              label: Text(
 | 
			
		||||
                publisherInvites.when(
 | 
			
		||||
                  data: (invites) => invites.length.toString(),
 | 
			
		||||
                  error: (_, _) => '0',
 | 
			
		||||
                  loading: () => '0',
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              isLabelVisible: publisherInvites.when(
 | 
			
		||||
                data: (invites) => invites.isNotEmpty,
 | 
			
		||||
                error: (_, _) => false,
 | 
			
		||||
                loading: () => false,
 | 
			
		||||
              ),
 | 
			
		||||
              child: const Icon(Symbols.email),
 | 
			
		||||
            ),
 | 
			
		||||
            onPressed: () {
 | 
			
		||||
              showModalBottomSheet(
 | 
			
		||||
                context: context,
 | 
			
		||||
                isScrollControlled: true,
 | 
			
		||||
                builder: (_) => const _PublisherInviteSheet(),
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
          DropdownButtonHideUnderline(
 | 
			
		||||
            child: DropdownButton2<SnPublisher>(
 | 
			
		||||
              alignment: Alignment.centerRight,
 | 
			
		||||
@@ -203,7 +304,7 @@ class CreatorHubScreen extends HookConsumerWidget {
 | 
			
		||||
                          ...(publishers.value?.map(
 | 
			
		||||
                                (publisher) => ListTile(
 | 
			
		||||
                                  leading: ProfilePictureWidget(
 | 
			
		||||
                                    fileId: publisher.picture?.id,
 | 
			
		||||
                                    file: publisher.picture,
 | 
			
		||||
                                  ),
 | 
			
		||||
                                  title: Text(publisher.nick),
 | 
			
		||||
                                  subtitle: Text('@${publisher.name}'),
 | 
			
		||||
@@ -266,6 +367,72 @@ class CreatorHubScreen extends HookConsumerWidget {
 | 
			
		||||
                              );
 | 
			
		||||
                            },
 | 
			
		||||
                          ),
 | 
			
		||||
                          ListTile(
 | 
			
		||||
                            minTileHeight: 48,
 | 
			
		||||
                            title: Text('publisherMembers').tr(),
 | 
			
		||||
                            trailing: Icon(Symbols.chevron_right),
 | 
			
		||||
                            leading: const Icon(Symbols.group),
 | 
			
		||||
                            contentPadding: EdgeInsets.symmetric(
 | 
			
		||||
                              horizontal: 24,
 | 
			
		||||
                            ),
 | 
			
		||||
                            onTap: () {
 | 
			
		||||
                              showModalBottomSheet(
 | 
			
		||||
                                isScrollControlled: true,
 | 
			
		||||
                                context: context,
 | 
			
		||||
                                builder:
 | 
			
		||||
                                    (context) => _PublisherMemberListSheet(
 | 
			
		||||
                                      publisherUname:
 | 
			
		||||
                                          currentPublisher.value!.name,
 | 
			
		||||
                                    ),
 | 
			
		||||
                              );
 | 
			
		||||
                            },
 | 
			
		||||
                          ),
 | 
			
		||||
                          ExpansionTile(
 | 
			
		||||
                            title: Text('publisherFeatures').tr(),
 | 
			
		||||
                            leading: const Icon(Symbols.flag),
 | 
			
		||||
                            tilePadding: EdgeInsets.symmetric(horizontal: 24),
 | 
			
		||||
                            minTileHeight: 48,
 | 
			
		||||
                            children: [
 | 
			
		||||
                              ...publisherFeatures.when(
 | 
			
		||||
                                data: (data) {
 | 
			
		||||
                                  return data.entries.map((entry) {
 | 
			
		||||
                                    final keyPrefix =
 | 
			
		||||
                                        'publisherFeature${entry.key.capitalizeEachWord()}';
 | 
			
		||||
                                    return ListTile(
 | 
			
		||||
                                      minTileHeight: 48,
 | 
			
		||||
                                      contentPadding: EdgeInsets.symmetric(
 | 
			
		||||
                                        horizontal: 24,
 | 
			
		||||
                                      ),
 | 
			
		||||
                                      leading: Icon(
 | 
			
		||||
                                        Symbols.circle,
 | 
			
		||||
                                        color:
 | 
			
		||||
                                            entry.value
 | 
			
		||||
                                                ? Colors.green
 | 
			
		||||
                                                : Colors.red,
 | 
			
		||||
                                        fill: 1,
 | 
			
		||||
                                        size: 16,
 | 
			
		||||
                                      ).padding(left: 2, top: 4),
 | 
			
		||||
                                      title: Text(keyPrefix).tr(),
 | 
			
		||||
                                      subtitle: Column(
 | 
			
		||||
                                        crossAxisAlignment:
 | 
			
		||||
                                            CrossAxisAlignment.start,
 | 
			
		||||
                                        children: [
 | 
			
		||||
                                          Text('${keyPrefix}Description').tr(),
 | 
			
		||||
                                          if (!entry.value)
 | 
			
		||||
                                            Text(
 | 
			
		||||
                                              '${keyPrefix}Hint',
 | 
			
		||||
                                            ).tr().bold(),
 | 
			
		||||
                                        ],
 | 
			
		||||
                                      ),
 | 
			
		||||
                                      isThreeLine: true,
 | 
			
		||||
                                    );
 | 
			
		||||
                                  }).toList();
 | 
			
		||||
                                },
 | 
			
		||||
                                error: (_, _) => [],
 | 
			
		||||
                                loading: () => [],
 | 
			
		||||
                              ),
 | 
			
		||||
                            ],
 | 
			
		||||
                          ),
 | 
			
		||||
                          Divider(height: 1).padding(vertical: 8),
 | 
			
		||||
                          ListTile(
 | 
			
		||||
                            minTileHeight: 48,
 | 
			
		||||
@@ -393,3 +560,482 @@ class _PublisherStatsWidget extends StatelessWidget {
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class PublisherMemberState {
 | 
			
		||||
  final List<SnPublisherMember> members;
 | 
			
		||||
  final bool isLoading;
 | 
			
		||||
  final int total;
 | 
			
		||||
  final String? error;
 | 
			
		||||
 | 
			
		||||
  const PublisherMemberState({
 | 
			
		||||
    required this.members,
 | 
			
		||||
    required this.isLoading,
 | 
			
		||||
    required this.total,
 | 
			
		||||
    this.error,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  PublisherMemberState copyWith({
 | 
			
		||||
    List<SnPublisherMember>? members,
 | 
			
		||||
    bool? isLoading,
 | 
			
		||||
    int? total,
 | 
			
		||||
    String? error,
 | 
			
		||||
  }) {
 | 
			
		||||
    return PublisherMemberState(
 | 
			
		||||
      members: members ?? this.members,
 | 
			
		||||
      isLoading: isLoading ?? this.isLoading,
 | 
			
		||||
      total: total ?? this.total,
 | 
			
		||||
      error: error ?? this.error,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
final publisherMemberStateProvider = StateNotifierProvider.family<
 | 
			
		||||
  PublisherMemberNotifier,
 | 
			
		||||
  PublisherMemberState,
 | 
			
		||||
  String
 | 
			
		||||
>((ref, publisherUname) {
 | 
			
		||||
  final apiClient = ref.watch(apiClientProvider);
 | 
			
		||||
  return PublisherMemberNotifier(apiClient, publisherUname);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
class PublisherMemberNotifier extends StateNotifier<PublisherMemberState> {
 | 
			
		||||
  final String publisherUname;
 | 
			
		||||
  final Dio _apiClient;
 | 
			
		||||
 | 
			
		||||
  PublisherMemberNotifier(this._apiClient, this.publisherUname)
 | 
			
		||||
    : super(
 | 
			
		||||
        const PublisherMemberState(members: [], isLoading: false, total: 0),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  Future<void> loadMore({int offset = 0, int take = 20}) async {
 | 
			
		||||
    if (state.isLoading) return;
 | 
			
		||||
    if (state.total > 0 && state.members.length >= state.total) return;
 | 
			
		||||
 | 
			
		||||
    state = state.copyWith(isLoading: true, error: null);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      final response = await _apiClient.get(
 | 
			
		||||
        '/publishers/$publisherUname/members',
 | 
			
		||||
        queryParameters: {'offset': offset, 'take': take},
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      final total = int.parse(response.headers.value('X-Total') ?? '0');
 | 
			
		||||
      final List<dynamic> data = response.data;
 | 
			
		||||
      final members = data.map((e) => SnPublisherMember.fromJson(e)).toList();
 | 
			
		||||
 | 
			
		||||
      state = state.copyWith(
 | 
			
		||||
        members: [...state.members, ...members],
 | 
			
		||||
        total: total,
 | 
			
		||||
        isLoading: false,
 | 
			
		||||
      );
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      state = state.copyWith(error: e.toString(), isLoading: false);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void reset() {
 | 
			
		||||
    state = const PublisherMemberState(members: [], isLoading: false, total: 0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _PublisherMemberListSheet extends HookConsumerWidget {
 | 
			
		||||
  final String publisherUname;
 | 
			
		||||
  const _PublisherMemberListSheet({required this.publisherUname});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    final publisherIdentity = ref.watch(
 | 
			
		||||
      publisherIdentityProvider(publisherUname),
 | 
			
		||||
    );
 | 
			
		||||
    final memberListProvider = publisherMemberListNotifierProvider(
 | 
			
		||||
      publisherUname,
 | 
			
		||||
    );
 | 
			
		||||
    final memberState = ref.watch(publisherMemberStateProvider(publisherUname));
 | 
			
		||||
    final memberNotifier = ref.read(
 | 
			
		||||
      publisherMemberStateProvider(publisherUname).notifier,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    useEffect(() {
 | 
			
		||||
      Future(() {
 | 
			
		||||
        memberNotifier.loadMore();
 | 
			
		||||
      });
 | 
			
		||||
      return null;
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    Future<void> invitePerson() async {
 | 
			
		||||
      final result = await showModalBottomSheet(
 | 
			
		||||
        isScrollControlled: true,
 | 
			
		||||
        context: context,
 | 
			
		||||
        builder: (context) => const AccountPickerSheet(),
 | 
			
		||||
      );
 | 
			
		||||
      if (result == null) return;
 | 
			
		||||
      try {
 | 
			
		||||
        final apiClient = ref.watch(apiClientProvider);
 | 
			
		||||
        await apiClient.post(
 | 
			
		||||
          '/publishers/$publisherUname/invites',
 | 
			
		||||
          data: {'related_user_id': result.id, 'role': 0},
 | 
			
		||||
        );
 | 
			
		||||
        ref.invalidate(memberListProvider);
 | 
			
		||||
      } catch (err) {
 | 
			
		||||
        showErrorAlert(err);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Container(
 | 
			
		||||
      constraints: BoxConstraints(
 | 
			
		||||
        maxHeight: MediaQuery.of(context).size.height * 0.8,
 | 
			
		||||
      ),
 | 
			
		||||
      child: Column(
 | 
			
		||||
        children: [
 | 
			
		||||
          Padding(
 | 
			
		||||
            padding: EdgeInsets.only(top: 16, left: 20, right: 16, bottom: 12),
 | 
			
		||||
            child: Row(
 | 
			
		||||
              children: [
 | 
			
		||||
                Text(
 | 
			
		||||
                  'members'.plural(memberState.total),
 | 
			
		||||
                  style: Theme.of(context).textTheme.headlineSmall?.copyWith(
 | 
			
		||||
                    fontWeight: FontWeight.w600,
 | 
			
		||||
                    letterSpacing: -0.5,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                const Spacer(),
 | 
			
		||||
                IconButton(
 | 
			
		||||
                  icon: const Icon(Symbols.person_add),
 | 
			
		||||
                  onPressed: invitePerson,
 | 
			
		||||
                  style: IconButton.styleFrom(minimumSize: const Size(36, 36)),
 | 
			
		||||
                ),
 | 
			
		||||
                IconButton(
 | 
			
		||||
                  icon: const Icon(Symbols.refresh),
 | 
			
		||||
                  onPressed: () {
 | 
			
		||||
                    memberNotifier.reset();
 | 
			
		||||
                    memberNotifier.loadMore();
 | 
			
		||||
                    ref.invalidate(memberListProvider);
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
                IconButton(
 | 
			
		||||
                  icon: const Icon(Symbols.close),
 | 
			
		||||
                  onPressed: () => Navigator.pop(context),
 | 
			
		||||
                  style: IconButton.styleFrom(minimumSize: const Size(36, 36)),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          const Divider(height: 1),
 | 
			
		||||
          Expanded(
 | 
			
		||||
            child: PagingHelperView(
 | 
			
		||||
              provider: memberListProvider,
 | 
			
		||||
              futureRefreshable: memberListProvider.future,
 | 
			
		||||
              notifierRefreshable: memberListProvider.notifier,
 | 
			
		||||
              contentBuilder: (data, widgetCount, endItemView) {
 | 
			
		||||
                return ListView.builder(
 | 
			
		||||
                  itemCount: widgetCount,
 | 
			
		||||
                  itemBuilder: (context, index) {
 | 
			
		||||
                    if (index == data.items.length) {
 | 
			
		||||
                      return endItemView;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    final member = data.items[index];
 | 
			
		||||
                    return ListTile(
 | 
			
		||||
                      contentPadding: EdgeInsets.only(left: 16, right: 12),
 | 
			
		||||
                      leading: ProfilePictureWidget(
 | 
			
		||||
                        fileId: member.account!.profile.picture?.id,
 | 
			
		||||
                      ),
 | 
			
		||||
                      title: Row(
 | 
			
		||||
                        spacing: 6,
 | 
			
		||||
                        children: [
 | 
			
		||||
                          Flexible(child: Text(member.account!.nick)),
 | 
			
		||||
                          if (member.joinedAt == null)
 | 
			
		||||
                            const Icon(Symbols.pending_actions, size: 20),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ),
 | 
			
		||||
                      subtitle: Row(
 | 
			
		||||
                        children: [
 | 
			
		||||
                          Text(
 | 
			
		||||
                            member.role >= 100
 | 
			
		||||
                                ? 'permissionOwner'
 | 
			
		||||
                                : member.role >= 50
 | 
			
		||||
                                ? 'permissionModerator'
 | 
			
		||||
                                : 'permissionMember',
 | 
			
		||||
                          ).tr(),
 | 
			
		||||
                          Text('·').bold().padding(horizontal: 6),
 | 
			
		||||
                          Expanded(child: Text("@${member.account!.name}")),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ),
 | 
			
		||||
                      trailing: Row(
 | 
			
		||||
                        mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                        children: [
 | 
			
		||||
                          if ((publisherIdentity.value?.role ?? 0) >= 50)
 | 
			
		||||
                            IconButton(
 | 
			
		||||
                              icon: const Icon(Symbols.edit),
 | 
			
		||||
                              onPressed: () {
 | 
			
		||||
                                showModalBottomSheet(
 | 
			
		||||
                                  isScrollControlled: true,
 | 
			
		||||
                                  context: context,
 | 
			
		||||
                                  builder:
 | 
			
		||||
                                      (context) => _PublisherMemberRoleSheet(
 | 
			
		||||
                                        publisherUname: publisherUname,
 | 
			
		||||
                                        member: member,
 | 
			
		||||
                                      ),
 | 
			
		||||
                                ).then((value) {
 | 
			
		||||
                                  if (value != null) {
 | 
			
		||||
                                    ref.invalidate(memberListProvider);
 | 
			
		||||
                                  }
 | 
			
		||||
                                });
 | 
			
		||||
                              },
 | 
			
		||||
                            ),
 | 
			
		||||
                          if ((publisherIdentity.value?.role ?? 0) >= 50)
 | 
			
		||||
                            IconButton(
 | 
			
		||||
                              icon: const Icon(Symbols.delete),
 | 
			
		||||
                              onPressed: () {
 | 
			
		||||
                                showConfirmAlert(
 | 
			
		||||
                                  'removePublisherMemberHint'.tr(),
 | 
			
		||||
                                  'removePublisherMember'.tr(),
 | 
			
		||||
                                ).then((confirm) async {
 | 
			
		||||
                                  if (confirm != true) return;
 | 
			
		||||
                                  try {
 | 
			
		||||
                                    final apiClient = ref.watch(
 | 
			
		||||
                                      apiClientProvider,
 | 
			
		||||
                                    );
 | 
			
		||||
                                    await apiClient.delete(
 | 
			
		||||
                                      '/publishers/$publisherUname/members/${member.accountId}',
 | 
			
		||||
                                    );
 | 
			
		||||
                                    ref.invalidate(memberListProvider);
 | 
			
		||||
                                  } catch (err) {
 | 
			
		||||
                                    showErrorAlert(err);
 | 
			
		||||
                                  }
 | 
			
		||||
                                });
 | 
			
		||||
                              },
 | 
			
		||||
                            ),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ),
 | 
			
		||||
                    );
 | 
			
		||||
                  },
 | 
			
		||||
                );
 | 
			
		||||
              },
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _PublisherMemberRoleSheet extends HookConsumerWidget {
 | 
			
		||||
  final String publisherUname;
 | 
			
		||||
  final SnPublisherMember member;
 | 
			
		||||
 | 
			
		||||
  const _PublisherMemberRoleSheet({
 | 
			
		||||
    required this.publisherUname,
 | 
			
		||||
    required this.member,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    final roleController = useTextEditingController(
 | 
			
		||||
      text: member.role.toString(),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return Container(
 | 
			
		||||
      padding: EdgeInsets.only(
 | 
			
		||||
        bottom: MediaQuery.of(context).viewInsets.bottom,
 | 
			
		||||
      ),
 | 
			
		||||
      child: SafeArea(
 | 
			
		||||
        child: Column(
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
			
		||||
          children: [
 | 
			
		||||
            Padding(
 | 
			
		||||
              padding: EdgeInsets.only(
 | 
			
		||||
                top: 16,
 | 
			
		||||
                left: 20,
 | 
			
		||||
                right: 16,
 | 
			
		||||
                bottom: 12,
 | 
			
		||||
              ),
 | 
			
		||||
              child: Row(
 | 
			
		||||
                children: [
 | 
			
		||||
                  Text(
 | 
			
		||||
                    'memberRoleEdit'.tr(args: [member.account!.name]),
 | 
			
		||||
                    style: Theme.of(context).textTheme.headlineSmall?.copyWith(
 | 
			
		||||
                      fontWeight: FontWeight.w600,
 | 
			
		||||
                      letterSpacing: -0.5,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                  const Spacer(),
 | 
			
		||||
                  IconButton(
 | 
			
		||||
                    icon: const Icon(Symbols.close),
 | 
			
		||||
                    onPressed: () => Navigator.pop(context),
 | 
			
		||||
                    style: IconButton.styleFrom(
 | 
			
		||||
                      minimumSize: const Size(36, 36),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            const Divider(height: 1),
 | 
			
		||||
            Column(
 | 
			
		||||
              crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
			
		||||
              children: [
 | 
			
		||||
                Autocomplete<int>(
 | 
			
		||||
                  optionsBuilder: (TextEditingValue textEditingValue) {
 | 
			
		||||
                    if (textEditingValue.text.isEmpty) {
 | 
			
		||||
                      return const [100, 50, 0];
 | 
			
		||||
                    }
 | 
			
		||||
                    final int? value = int.tryParse(textEditingValue.text);
 | 
			
		||||
                    if (value == null) return const [100, 50, 0];
 | 
			
		||||
                    return [100, 50, 0].where(
 | 
			
		||||
                      (option) =>
 | 
			
		||||
                          option.toString().contains(textEditingValue.text),
 | 
			
		||||
                    );
 | 
			
		||||
                  },
 | 
			
		||||
                  onSelected: (int selection) {
 | 
			
		||||
                    roleController.text = selection.toString();
 | 
			
		||||
                  },
 | 
			
		||||
                  fieldViewBuilder: (
 | 
			
		||||
                    context,
 | 
			
		||||
                    controller,
 | 
			
		||||
                    focusNode,
 | 
			
		||||
                    onFieldSubmitted,
 | 
			
		||||
                  ) {
 | 
			
		||||
                    return TextField(
 | 
			
		||||
                      controller: controller,
 | 
			
		||||
                      focusNode: focusNode,
 | 
			
		||||
                      keyboardType: TextInputType.number,
 | 
			
		||||
                      decoration: InputDecoration(
 | 
			
		||||
                        labelText: 'memberRole'.tr(),
 | 
			
		||||
                        helperText: 'memberRoleHint'.tr(),
 | 
			
		||||
                      ),
 | 
			
		||||
                      onTapOutside: (event) => focusNode.unfocus(),
 | 
			
		||||
                    );
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
                const Gap(16),
 | 
			
		||||
                FilledButton.icon(
 | 
			
		||||
                  onPressed: () async {
 | 
			
		||||
                    try {
 | 
			
		||||
                      final newRole = int.parse(roleController.text);
 | 
			
		||||
                      if (newRole < 0 || newRole > 100) {
 | 
			
		||||
                        throw 'Role must be between 0 and 100';
 | 
			
		||||
                      }
 | 
			
		||||
 | 
			
		||||
                      final apiClient = ref.read(apiClientProvider);
 | 
			
		||||
                      await apiClient.patch(
 | 
			
		||||
                        '/publishers/$publisherUname/members/${member.accountId}/role',
 | 
			
		||||
                        data: newRole,
 | 
			
		||||
                      );
 | 
			
		||||
 | 
			
		||||
                      if (context.mounted) Navigator.pop(context, true);
 | 
			
		||||
                    } catch (err) {
 | 
			
		||||
                      showErrorAlert(err);
 | 
			
		||||
                    }
 | 
			
		||||
                  },
 | 
			
		||||
                  icon: const Icon(Symbols.save),
 | 
			
		||||
                  label: const Text('saveChanges').tr(),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ).padding(vertical: 16, horizontal: 24),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _PublisherInviteSheet extends HookConsumerWidget {
 | 
			
		||||
  const _PublisherInviteSheet();
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    final invites = ref.watch(publisherInvitesProvider);
 | 
			
		||||
 | 
			
		||||
    Future<void> acceptInvite(SnPublisherMember invite) async {
 | 
			
		||||
      try {
 | 
			
		||||
        final client = ref.read(apiClientProvider);
 | 
			
		||||
        await client.post(
 | 
			
		||||
          '/publishers/invites/${invite.publisher!.name}/accept',
 | 
			
		||||
        );
 | 
			
		||||
        ref.invalidate(publisherInvitesProvider);
 | 
			
		||||
        ref.invalidate(publishersManagedProvider);
 | 
			
		||||
      } catch (err) {
 | 
			
		||||
        showErrorAlert(err);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Future<void> declineInvite(SnPublisherMember invite) async {
 | 
			
		||||
      try {
 | 
			
		||||
        final client = ref.read(apiClientProvider);
 | 
			
		||||
        await client.post(
 | 
			
		||||
          '/publishers/invites/${invite.publisher!.name}/decline',
 | 
			
		||||
        );
 | 
			
		||||
        ref.invalidate(publisherInvitesProvider);
 | 
			
		||||
      } catch (err) {
 | 
			
		||||
        showErrorAlert(err);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return SheetScaffold(
 | 
			
		||||
      titleText: 'invites'.tr(),
 | 
			
		||||
      actions: [
 | 
			
		||||
        IconButton(
 | 
			
		||||
          icon: const Icon(Symbols.refresh),
 | 
			
		||||
          style: IconButton.styleFrom(minimumSize: const Size(36, 36)),
 | 
			
		||||
          onPressed: () {
 | 
			
		||||
            ref.invalidate(publisherInvitesProvider);
 | 
			
		||||
          },
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
      child: invites.when(
 | 
			
		||||
        data:
 | 
			
		||||
            (items) =>
 | 
			
		||||
                items.isEmpty
 | 
			
		||||
                    ? Center(
 | 
			
		||||
                      child:
 | 
			
		||||
                          Text(
 | 
			
		||||
                            'invitesEmpty',
 | 
			
		||||
                            textAlign: TextAlign.center,
 | 
			
		||||
                          ).tr(),
 | 
			
		||||
                    )
 | 
			
		||||
                    : ListView.builder(
 | 
			
		||||
                      shrinkWrap: true,
 | 
			
		||||
                      itemCount: items.length,
 | 
			
		||||
                      itemBuilder: (context, index) {
 | 
			
		||||
                        final invite = items[index];
 | 
			
		||||
                        return ListTile(
 | 
			
		||||
                          leading: ProfilePictureWidget(
 | 
			
		||||
                            fileId: invite.publisher!.picture?.id,
 | 
			
		||||
                            fallbackIcon: Symbols.group,
 | 
			
		||||
                          ),
 | 
			
		||||
                          title: Text(invite.publisher!.nick),
 | 
			
		||||
                          subtitle:
 | 
			
		||||
                              Text(
 | 
			
		||||
                                invite.role >= 100
 | 
			
		||||
                                    ? 'permissionOwner'
 | 
			
		||||
                                    : invite.role >= 50
 | 
			
		||||
                                    ? 'permissionModerator'
 | 
			
		||||
                                    : 'permissionMember',
 | 
			
		||||
                              ).tr(),
 | 
			
		||||
                          trailing: Row(
 | 
			
		||||
                            mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                            children: [
 | 
			
		||||
                              IconButton(
 | 
			
		||||
                                icon: const Icon(Symbols.check),
 | 
			
		||||
                                onPressed: () => acceptInvite(invite),
 | 
			
		||||
                              ),
 | 
			
		||||
                              IconButton(
 | 
			
		||||
                                icon: const Icon(Symbols.close),
 | 
			
		||||
                                onPressed: () => declineInvite(invite),
 | 
			
		||||
                              ),
 | 
			
		||||
                            ],
 | 
			
		||||
                          ),
 | 
			
		||||
                        );
 | 
			
		||||
                      },
 | 
			
		||||
                    ),
 | 
			
		||||
        loading: () => const Center(child: CircularProgressIndicator()),
 | 
			
		||||
        error:
 | 
			
		||||
            (error, _) => ResponseErrorWidget(
 | 
			
		||||
              error: error,
 | 
			
		||||
              onRetry: () => ref.invalidate(publisherInvitesProvider),
 | 
			
		||||
            ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -149,5 +149,422 @@ class _PublisherStatsProviderElement
 | 
			
		||||
  String? get uname => (origin as PublisherStatsProvider).uname;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String _$publisherIdentityHash() => r'f7fd986a303a729ca5557022fceb37cd01fa17f3';
 | 
			
		||||
 | 
			
		||||
/// See also [publisherIdentity].
 | 
			
		||||
@ProviderFor(publisherIdentity)
 | 
			
		||||
const publisherIdentityProvider = PublisherIdentityFamily();
 | 
			
		||||
 | 
			
		||||
/// See also [publisherIdentity].
 | 
			
		||||
class PublisherIdentityFamily extends Family<AsyncValue<SnPublisherMember?>> {
 | 
			
		||||
  /// See also [publisherIdentity].
 | 
			
		||||
  const PublisherIdentityFamily();
 | 
			
		||||
 | 
			
		||||
  /// See also [publisherIdentity].
 | 
			
		||||
  PublisherIdentityProvider call(String uname) {
 | 
			
		||||
    return PublisherIdentityProvider(uname);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  PublisherIdentityProvider getProviderOverride(
 | 
			
		||||
    covariant PublisherIdentityProvider provider,
 | 
			
		||||
  ) {
 | 
			
		||||
    return call(provider.uname);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static const Iterable<ProviderOrFamily>? _dependencies = null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Iterable<ProviderOrFamily>? get dependencies => _dependencies;
 | 
			
		||||
 | 
			
		||||
  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
 | 
			
		||||
      _allTransitiveDependencies;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String? get name => r'publisherIdentityProvider';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// See also [publisherIdentity].
 | 
			
		||||
class PublisherIdentityProvider
 | 
			
		||||
    extends AutoDisposeFutureProvider<SnPublisherMember?> {
 | 
			
		||||
  /// See also [publisherIdentity].
 | 
			
		||||
  PublisherIdentityProvider(String uname)
 | 
			
		||||
    : this._internal(
 | 
			
		||||
        (ref) => publisherIdentity(ref as PublisherIdentityRef, uname),
 | 
			
		||||
        from: publisherIdentityProvider,
 | 
			
		||||
        name: r'publisherIdentityProvider',
 | 
			
		||||
        debugGetCreateSourceHash:
 | 
			
		||||
            const bool.fromEnvironment('dart.vm.product')
 | 
			
		||||
                ? null
 | 
			
		||||
                : _$publisherIdentityHash,
 | 
			
		||||
        dependencies: PublisherIdentityFamily._dependencies,
 | 
			
		||||
        allTransitiveDependencies:
 | 
			
		||||
            PublisherIdentityFamily._allTransitiveDependencies,
 | 
			
		||||
        uname: uname,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  PublisherIdentityProvider._internal(
 | 
			
		||||
    super._createNotifier, {
 | 
			
		||||
    required super.name,
 | 
			
		||||
    required super.dependencies,
 | 
			
		||||
    required super.allTransitiveDependencies,
 | 
			
		||||
    required super.debugGetCreateSourceHash,
 | 
			
		||||
    required super.from,
 | 
			
		||||
    required this.uname,
 | 
			
		||||
  }) : super.internal();
 | 
			
		||||
 | 
			
		||||
  final String uname;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Override overrideWith(
 | 
			
		||||
    FutureOr<SnPublisherMember?> Function(PublisherIdentityRef provider) create,
 | 
			
		||||
  ) {
 | 
			
		||||
    return ProviderOverride(
 | 
			
		||||
      origin: this,
 | 
			
		||||
      override: PublisherIdentityProvider._internal(
 | 
			
		||||
        (ref) => create(ref as PublisherIdentityRef),
 | 
			
		||||
        from: from,
 | 
			
		||||
        name: null,
 | 
			
		||||
        dependencies: null,
 | 
			
		||||
        allTransitiveDependencies: null,
 | 
			
		||||
        debugGetCreateSourceHash: null,
 | 
			
		||||
        uname: uname,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  AutoDisposeFutureProviderElement<SnPublisherMember?> createElement() {
 | 
			
		||||
    return _PublisherIdentityProviderElement(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  bool operator ==(Object other) {
 | 
			
		||||
    return other is PublisherIdentityProvider && other.uname == uname;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode {
 | 
			
		||||
    var hash = _SystemHash.combine(0, runtimeType.hashCode);
 | 
			
		||||
    hash = _SystemHash.combine(hash, uname.hashCode);
 | 
			
		||||
 | 
			
		||||
    return _SystemHash.finish(hash);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
 | 
			
		||||
// ignore: unused_element
 | 
			
		||||
mixin PublisherIdentityRef on AutoDisposeFutureProviderRef<SnPublisherMember?> {
 | 
			
		||||
  /// The parameter `uname` of this provider.
 | 
			
		||||
  String get uname;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _PublisherIdentityProviderElement
 | 
			
		||||
    extends AutoDisposeFutureProviderElement<SnPublisherMember?>
 | 
			
		||||
    with PublisherIdentityRef {
 | 
			
		||||
  _PublisherIdentityProviderElement(super.provider);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String get uname => (origin as PublisherIdentityProvider).uname;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String _$publisherFeaturesHash() => r'34db65d9a4b6b0c6961733ae79e67f25d5d111d3';
 | 
			
		||||
 | 
			
		||||
/// See also [publisherFeatures].
 | 
			
		||||
@ProviderFor(publisherFeatures)
 | 
			
		||||
const publisherFeaturesProvider = PublisherFeaturesFamily();
 | 
			
		||||
 | 
			
		||||
/// See also [publisherFeatures].
 | 
			
		||||
class PublisherFeaturesFamily extends Family<AsyncValue<Map<String, bool>>> {
 | 
			
		||||
  /// See also [publisherFeatures].
 | 
			
		||||
  const PublisherFeaturesFamily();
 | 
			
		||||
 | 
			
		||||
  /// See also [publisherFeatures].
 | 
			
		||||
  PublisherFeaturesProvider call(String? uname) {
 | 
			
		||||
    return PublisherFeaturesProvider(uname);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  PublisherFeaturesProvider getProviderOverride(
 | 
			
		||||
    covariant PublisherFeaturesProvider provider,
 | 
			
		||||
  ) {
 | 
			
		||||
    return call(provider.uname);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static const Iterable<ProviderOrFamily>? _dependencies = null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Iterable<ProviderOrFamily>? get dependencies => _dependencies;
 | 
			
		||||
 | 
			
		||||
  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
 | 
			
		||||
      _allTransitiveDependencies;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String? get name => r'publisherFeaturesProvider';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// See also [publisherFeatures].
 | 
			
		||||
class PublisherFeaturesProvider
 | 
			
		||||
    extends AutoDisposeFutureProvider<Map<String, bool>> {
 | 
			
		||||
  /// See also [publisherFeatures].
 | 
			
		||||
  PublisherFeaturesProvider(String? uname)
 | 
			
		||||
    : this._internal(
 | 
			
		||||
        (ref) => publisherFeatures(ref as PublisherFeaturesRef, uname),
 | 
			
		||||
        from: publisherFeaturesProvider,
 | 
			
		||||
        name: r'publisherFeaturesProvider',
 | 
			
		||||
        debugGetCreateSourceHash:
 | 
			
		||||
            const bool.fromEnvironment('dart.vm.product')
 | 
			
		||||
                ? null
 | 
			
		||||
                : _$publisherFeaturesHash,
 | 
			
		||||
        dependencies: PublisherFeaturesFamily._dependencies,
 | 
			
		||||
        allTransitiveDependencies:
 | 
			
		||||
            PublisherFeaturesFamily._allTransitiveDependencies,
 | 
			
		||||
        uname: uname,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  PublisherFeaturesProvider._internal(
 | 
			
		||||
    super._createNotifier, {
 | 
			
		||||
    required super.name,
 | 
			
		||||
    required super.dependencies,
 | 
			
		||||
    required super.allTransitiveDependencies,
 | 
			
		||||
    required super.debugGetCreateSourceHash,
 | 
			
		||||
    required super.from,
 | 
			
		||||
    required this.uname,
 | 
			
		||||
  }) : super.internal();
 | 
			
		||||
 | 
			
		||||
  final String? uname;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Override overrideWith(
 | 
			
		||||
    FutureOr<Map<String, bool>> Function(PublisherFeaturesRef provider) create,
 | 
			
		||||
  ) {
 | 
			
		||||
    return ProviderOverride(
 | 
			
		||||
      origin: this,
 | 
			
		||||
      override: PublisherFeaturesProvider._internal(
 | 
			
		||||
        (ref) => create(ref as PublisherFeaturesRef),
 | 
			
		||||
        from: from,
 | 
			
		||||
        name: null,
 | 
			
		||||
        dependencies: null,
 | 
			
		||||
        allTransitiveDependencies: null,
 | 
			
		||||
        debugGetCreateSourceHash: null,
 | 
			
		||||
        uname: uname,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  AutoDisposeFutureProviderElement<Map<String, bool>> createElement() {
 | 
			
		||||
    return _PublisherFeaturesProviderElement(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  bool operator ==(Object other) {
 | 
			
		||||
    return other is PublisherFeaturesProvider && other.uname == uname;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode {
 | 
			
		||||
    var hash = _SystemHash.combine(0, runtimeType.hashCode);
 | 
			
		||||
    hash = _SystemHash.combine(hash, uname.hashCode);
 | 
			
		||||
 | 
			
		||||
    return _SystemHash.finish(hash);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
 | 
			
		||||
// ignore: unused_element
 | 
			
		||||
mixin PublisherFeaturesRef on AutoDisposeFutureProviderRef<Map<String, bool>> {
 | 
			
		||||
  /// The parameter `uname` of this provider.
 | 
			
		||||
  String? get uname;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _PublisherFeaturesProviderElement
 | 
			
		||||
    extends AutoDisposeFutureProviderElement<Map<String, bool>>
 | 
			
		||||
    with PublisherFeaturesRef {
 | 
			
		||||
  _PublisherFeaturesProviderElement(super.provider);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String? get uname => (origin as PublisherFeaturesProvider).uname;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String _$publisherInvitesHash() => r'488cd443407895ce11f4edff07cb6ea58f2aa018';
 | 
			
		||||
 | 
			
		||||
/// See also [publisherInvites].
 | 
			
		||||
@ProviderFor(publisherInvites)
 | 
			
		||||
final publisherInvitesProvider =
 | 
			
		||||
    AutoDisposeFutureProvider<List<SnPublisherMember>>.internal(
 | 
			
		||||
      publisherInvites,
 | 
			
		||||
      name: r'publisherInvitesProvider',
 | 
			
		||||
      debugGetCreateSourceHash:
 | 
			
		||||
          const bool.fromEnvironment('dart.vm.product')
 | 
			
		||||
              ? null
 | 
			
		||||
              : _$publisherInvitesHash,
 | 
			
		||||
      dependencies: null,
 | 
			
		||||
      allTransitiveDependencies: null,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
 | 
			
		||||
// ignore: unused_element
 | 
			
		||||
typedef PublisherInvitesRef =
 | 
			
		||||
    AutoDisposeFutureProviderRef<List<SnPublisherMember>>;
 | 
			
		||||
String _$publisherMemberListNotifierHash() =>
 | 
			
		||||
    r'237e8f39c9757a6cbdff817853c697539242ad2a';
 | 
			
		||||
 | 
			
		||||
abstract class _$PublisherMemberListNotifier
 | 
			
		||||
    extends
 | 
			
		||||
        BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnPublisherMember>> {
 | 
			
		||||
  late final String uname;
 | 
			
		||||
 | 
			
		||||
  FutureOr<CursorPagingData<SnPublisherMember>> build(String uname);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// See also [PublisherMemberListNotifier].
 | 
			
		||||
@ProviderFor(PublisherMemberListNotifier)
 | 
			
		||||
const publisherMemberListNotifierProvider = PublisherMemberListNotifierFamily();
 | 
			
		||||
 | 
			
		||||
/// See also [PublisherMemberListNotifier].
 | 
			
		||||
class PublisherMemberListNotifierFamily
 | 
			
		||||
    extends Family<AsyncValue<CursorPagingData<SnPublisherMember>>> {
 | 
			
		||||
  /// See also [PublisherMemberListNotifier].
 | 
			
		||||
  const PublisherMemberListNotifierFamily();
 | 
			
		||||
 | 
			
		||||
  /// See also [PublisherMemberListNotifier].
 | 
			
		||||
  PublisherMemberListNotifierProvider call(String uname) {
 | 
			
		||||
    return PublisherMemberListNotifierProvider(uname);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  PublisherMemberListNotifierProvider getProviderOverride(
 | 
			
		||||
    covariant PublisherMemberListNotifierProvider provider,
 | 
			
		||||
  ) {
 | 
			
		||||
    return call(provider.uname);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static const Iterable<ProviderOrFamily>? _dependencies = null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Iterable<ProviderOrFamily>? get dependencies => _dependencies;
 | 
			
		||||
 | 
			
		||||
  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
 | 
			
		||||
      _allTransitiveDependencies;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String? get name => r'publisherMemberListNotifierProvider';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// See also [PublisherMemberListNotifier].
 | 
			
		||||
class PublisherMemberListNotifierProvider
 | 
			
		||||
    extends
 | 
			
		||||
        AutoDisposeAsyncNotifierProviderImpl<
 | 
			
		||||
          PublisherMemberListNotifier,
 | 
			
		||||
          CursorPagingData<SnPublisherMember>
 | 
			
		||||
        > {
 | 
			
		||||
  /// See also [PublisherMemberListNotifier].
 | 
			
		||||
  PublisherMemberListNotifierProvider(String uname)
 | 
			
		||||
    : this._internal(
 | 
			
		||||
        () => PublisherMemberListNotifier()..uname = uname,
 | 
			
		||||
        from: publisherMemberListNotifierProvider,
 | 
			
		||||
        name: r'publisherMemberListNotifierProvider',
 | 
			
		||||
        debugGetCreateSourceHash:
 | 
			
		||||
            const bool.fromEnvironment('dart.vm.product')
 | 
			
		||||
                ? null
 | 
			
		||||
                : _$publisherMemberListNotifierHash,
 | 
			
		||||
        dependencies: PublisherMemberListNotifierFamily._dependencies,
 | 
			
		||||
        allTransitiveDependencies:
 | 
			
		||||
            PublisherMemberListNotifierFamily._allTransitiveDependencies,
 | 
			
		||||
        uname: uname,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  PublisherMemberListNotifierProvider._internal(
 | 
			
		||||
    super._createNotifier, {
 | 
			
		||||
    required super.name,
 | 
			
		||||
    required super.dependencies,
 | 
			
		||||
    required super.allTransitiveDependencies,
 | 
			
		||||
    required super.debugGetCreateSourceHash,
 | 
			
		||||
    required super.from,
 | 
			
		||||
    required this.uname,
 | 
			
		||||
  }) : super.internal();
 | 
			
		||||
 | 
			
		||||
  final String uname;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOr<CursorPagingData<SnPublisherMember>> runNotifierBuild(
 | 
			
		||||
    covariant PublisherMemberListNotifier notifier,
 | 
			
		||||
  ) {
 | 
			
		||||
    return notifier.build(uname);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Override overrideWith(PublisherMemberListNotifier Function() create) {
 | 
			
		||||
    return ProviderOverride(
 | 
			
		||||
      origin: this,
 | 
			
		||||
      override: PublisherMemberListNotifierProvider._internal(
 | 
			
		||||
        () => create()..uname = uname,
 | 
			
		||||
        from: from,
 | 
			
		||||
        name: null,
 | 
			
		||||
        dependencies: null,
 | 
			
		||||
        allTransitiveDependencies: null,
 | 
			
		||||
        debugGetCreateSourceHash: null,
 | 
			
		||||
        uname: uname,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  AutoDisposeAsyncNotifierProviderElement<
 | 
			
		||||
    PublisherMemberListNotifier,
 | 
			
		||||
    CursorPagingData<SnPublisherMember>
 | 
			
		||||
  >
 | 
			
		||||
  createElement() {
 | 
			
		||||
    return _PublisherMemberListNotifierProviderElement(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  bool operator ==(Object other) {
 | 
			
		||||
    return other is PublisherMemberListNotifierProvider && other.uname == uname;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode {
 | 
			
		||||
    var hash = _SystemHash.combine(0, runtimeType.hashCode);
 | 
			
		||||
    hash = _SystemHash.combine(hash, uname.hashCode);
 | 
			
		||||
 | 
			
		||||
    return _SystemHash.finish(hash);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
 | 
			
		||||
// ignore: unused_element
 | 
			
		||||
mixin PublisherMemberListNotifierRef
 | 
			
		||||
    on
 | 
			
		||||
        AutoDisposeAsyncNotifierProviderRef<
 | 
			
		||||
          CursorPagingData<SnPublisherMember>
 | 
			
		||||
        > {
 | 
			
		||||
  /// The parameter `uname` of this provider.
 | 
			
		||||
  String get uname;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _PublisherMemberListNotifierProviderElement
 | 
			
		||||
    extends
 | 
			
		||||
        AutoDisposeAsyncNotifierProviderElement<
 | 
			
		||||
          PublisherMemberListNotifier,
 | 
			
		||||
          CursorPagingData<SnPublisherMember>
 | 
			
		||||
        >
 | 
			
		||||
    with PublisherMemberListNotifierRef {
 | 
			
		||||
  _PublisherMemberListNotifierProviderElement(super.provider);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String get uname => (origin as PublisherMemberListNotifierProvider).uname;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ import 'package:go_router/go_router.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:image_picker/image_picker.dart';
 | 
			
		||||
import 'package:island/models/file.dart';
 | 
			
		||||
import 'package:island/models/post.dart';
 | 
			
		||||
import 'package:island/models/publisher.dart';
 | 
			
		||||
import 'package:island/models/realm.dart';
 | 
			
		||||
import 'package:island/pods/config.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										162
									
								
								lib/screens/developers/apps.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								lib/screens/developers/apps.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,162 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:go_router/go_router.dart';
 | 
			
		||||
import 'package:google_fonts/google_fonts.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/custom_app.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
import 'package:island/widgets/alert.dart';
 | 
			
		||||
import 'package:island/widgets/app_scaffold.dart';
 | 
			
		||||
import 'package:island/widgets/content/cloud_files.dart';
 | 
			
		||||
import 'package:island/widgets/response.dart';
 | 
			
		||||
import 'package:material_symbols_icons/symbols.dart';
 | 
			
		||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
			
		||||
import 'package:styled_widget/styled_widget.dart';
 | 
			
		||||
 | 
			
		||||
part 'apps.g.dart';
 | 
			
		||||
 | 
			
		||||
@riverpod
 | 
			
		||||
Future<List<CustomApp>> customApps(Ref ref, String publisherName) async {
 | 
			
		||||
  final client = ref.watch(apiClientProvider);
 | 
			
		||||
  final resp = await client.get('/developers/$publisherName/apps');
 | 
			
		||||
  return resp.data.map((e) => CustomApp.fromJson(e)).cast<CustomApp>().toList();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class CustomAppsScreen extends HookConsumerWidget {
 | 
			
		||||
  final String publisherName;
 | 
			
		||||
  const CustomAppsScreen({super.key, required this.publisherName});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    final apps = ref.watch(customAppsProvider(publisherName));
 | 
			
		||||
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        title: Text('customApps').tr(),
 | 
			
		||||
        actions: [
 | 
			
		||||
          IconButton(
 | 
			
		||||
            icon: const Icon(Symbols.add),
 | 
			
		||||
            onPressed: () {
 | 
			
		||||
              context.push('/developers/$publisherName/apps/new');
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
      body: apps.when(
 | 
			
		||||
        data: (data) {
 | 
			
		||||
          if (data.isEmpty) {
 | 
			
		||||
            return Center(child: Text('noCustomApps').tr());
 | 
			
		||||
          }
 | 
			
		||||
          return RefreshIndicator(
 | 
			
		||||
            onRefresh:
 | 
			
		||||
                () => ref.refresh(customAppsProvider(publisherName).future),
 | 
			
		||||
            child: ListView.builder(
 | 
			
		||||
              padding: EdgeInsets.only(top: 4),
 | 
			
		||||
              itemCount: data.length,
 | 
			
		||||
              itemBuilder: (context, index) {
 | 
			
		||||
                final app = data[index];
 | 
			
		||||
                return Card(
 | 
			
		||||
                  margin: const EdgeInsets.all(8.0),
 | 
			
		||||
                  child: Column(
 | 
			
		||||
                    children: [
 | 
			
		||||
                      SizedBox(
 | 
			
		||||
                        height: 150,
 | 
			
		||||
                        child: Stack(
 | 
			
		||||
                          fit: StackFit.expand,
 | 
			
		||||
                          children: [
 | 
			
		||||
                            if (app.background != null)
 | 
			
		||||
                              CloudFileWidget(
 | 
			
		||||
                                item: app.background!,
 | 
			
		||||
                                fit: BoxFit.cover,
 | 
			
		||||
                              ).clipRRect(topLeft: 8, topRight: 8),
 | 
			
		||||
                            if (app.picture != null)
 | 
			
		||||
                              Positioned(
 | 
			
		||||
                                left: 16,
 | 
			
		||||
                                bottom: 16,
 | 
			
		||||
                                child: ProfilePictureWidget(
 | 
			
		||||
                                  fileId: app.picture!.id,
 | 
			
		||||
                                  radius: 40,
 | 
			
		||||
                                  fallbackIcon: Symbols.apps,
 | 
			
		||||
                                ),
 | 
			
		||||
                              ),
 | 
			
		||||
                          ],
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                      ListTile(
 | 
			
		||||
                        title: Text(app.name),
 | 
			
		||||
                        subtitle: Text(
 | 
			
		||||
                          app.slug,
 | 
			
		||||
                          style: GoogleFonts.robotoMono(fontSize: 12),
 | 
			
		||||
                        ),
 | 
			
		||||
                        contentPadding: EdgeInsets.only(left: 20, right: 12),
 | 
			
		||||
                        trailing: PopupMenuButton(
 | 
			
		||||
                          itemBuilder:
 | 
			
		||||
                              (context) => [
 | 
			
		||||
                                PopupMenuItem(
 | 
			
		||||
                                  value: 'edit',
 | 
			
		||||
                                  child: Row(
 | 
			
		||||
                                    children: [
 | 
			
		||||
                                      const Icon(Symbols.edit),
 | 
			
		||||
                                      const SizedBox(width: 12),
 | 
			
		||||
                                      Text('edit').tr(),
 | 
			
		||||
                                    ],
 | 
			
		||||
                                  ),
 | 
			
		||||
                                ),
 | 
			
		||||
                                PopupMenuItem(
 | 
			
		||||
                                  value: 'delete',
 | 
			
		||||
                                  child: Row(
 | 
			
		||||
                                    children: [
 | 
			
		||||
                                      const Icon(
 | 
			
		||||
                                        Symbols.delete,
 | 
			
		||||
                                        color: Colors.red,
 | 
			
		||||
                                      ),
 | 
			
		||||
                                      const SizedBox(width: 12),
 | 
			
		||||
                                      Text(
 | 
			
		||||
                                        'delete',
 | 
			
		||||
                                        style: TextStyle(color: Colors.red),
 | 
			
		||||
                                      ).tr(),
 | 
			
		||||
                                    ],
 | 
			
		||||
                                  ),
 | 
			
		||||
                                ),
 | 
			
		||||
                              ],
 | 
			
		||||
                          onSelected: (value) {
 | 
			
		||||
                            if (value == 'edit') {
 | 
			
		||||
                              context.push(
 | 
			
		||||
                                '/developers/$publisherName/apps/${app.id}',
 | 
			
		||||
                              );
 | 
			
		||||
                            } else if (value == 'delete') {
 | 
			
		||||
                              showConfirmAlert(
 | 
			
		||||
                                'deleteCustomAppHint'.tr(),
 | 
			
		||||
                                'deleteCustomApp'.tr(),
 | 
			
		||||
                              ).then((confirm) {
 | 
			
		||||
                                if (confirm) {
 | 
			
		||||
                                  final client = ref.read(apiClientProvider);
 | 
			
		||||
                                  client.delete(
 | 
			
		||||
                                    '/developers/$publisherName/apps/${app.id}',
 | 
			
		||||
                                  );
 | 
			
		||||
                                  ref.invalidate(
 | 
			
		||||
                                    customAppsProvider(publisherName),
 | 
			
		||||
                                  );
 | 
			
		||||
                                }
 | 
			
		||||
                              });
 | 
			
		||||
                            }
 | 
			
		||||
                          },
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
                );
 | 
			
		||||
              },
 | 
			
		||||
            ),
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
        loading: () => const Center(child: CircularProgressIndicator()),
 | 
			
		||||
        error:
 | 
			
		||||
            (err, stack) => ResponseErrorWidget(
 | 
			
		||||
              error: err,
 | 
			
		||||
              onRetry: () => ref.invalidate(customAppsProvider(publisherName)),
 | 
			
		||||
            ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										151
									
								
								lib/screens/developers/apps.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								lib/screens/developers/apps.g.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
			
		||||
// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
			
		||||
 | 
			
		||||
part of 'apps.dart';
 | 
			
		||||
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
// RiverpodGenerator
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
 | 
			
		||||
String _$customAppsHash() => r'1dec11573b9d987c3adbdf4732b3781a6f40172a';
 | 
			
		||||
 | 
			
		||||
/// 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 [customApps].
 | 
			
		||||
@ProviderFor(customApps)
 | 
			
		||||
const customAppsProvider = CustomAppsFamily();
 | 
			
		||||
 | 
			
		||||
/// See also [customApps].
 | 
			
		||||
class CustomAppsFamily extends Family<AsyncValue<List<CustomApp>>> {
 | 
			
		||||
  /// See also [customApps].
 | 
			
		||||
  const CustomAppsFamily();
 | 
			
		||||
 | 
			
		||||
  /// See also [customApps].
 | 
			
		||||
  CustomAppsProvider call(String publisherName) {
 | 
			
		||||
    return CustomAppsProvider(publisherName);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  CustomAppsProvider getProviderOverride(
 | 
			
		||||
    covariant CustomAppsProvider provider,
 | 
			
		||||
  ) {
 | 
			
		||||
    return call(provider.publisherName);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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'customAppsProvider';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// See also [customApps].
 | 
			
		||||
class CustomAppsProvider extends AutoDisposeFutureProvider<List<CustomApp>> {
 | 
			
		||||
  /// See also [customApps].
 | 
			
		||||
  CustomAppsProvider(String publisherName)
 | 
			
		||||
    : this._internal(
 | 
			
		||||
        (ref) => customApps(ref as CustomAppsRef, publisherName),
 | 
			
		||||
        from: customAppsProvider,
 | 
			
		||||
        name: r'customAppsProvider',
 | 
			
		||||
        debugGetCreateSourceHash:
 | 
			
		||||
            const bool.fromEnvironment('dart.vm.product')
 | 
			
		||||
                ? null
 | 
			
		||||
                : _$customAppsHash,
 | 
			
		||||
        dependencies: CustomAppsFamily._dependencies,
 | 
			
		||||
        allTransitiveDependencies: CustomAppsFamily._allTransitiveDependencies,
 | 
			
		||||
        publisherName: publisherName,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  CustomAppsProvider._internal(
 | 
			
		||||
    super._createNotifier, {
 | 
			
		||||
    required super.name,
 | 
			
		||||
    required super.dependencies,
 | 
			
		||||
    required super.allTransitiveDependencies,
 | 
			
		||||
    required super.debugGetCreateSourceHash,
 | 
			
		||||
    required super.from,
 | 
			
		||||
    required this.publisherName,
 | 
			
		||||
  }) : super.internal();
 | 
			
		||||
 | 
			
		||||
  final String publisherName;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Override overrideWith(
 | 
			
		||||
    FutureOr<List<CustomApp>> Function(CustomAppsRef provider) create,
 | 
			
		||||
  ) {
 | 
			
		||||
    return ProviderOverride(
 | 
			
		||||
      origin: this,
 | 
			
		||||
      override: CustomAppsProvider._internal(
 | 
			
		||||
        (ref) => create(ref as CustomAppsRef),
 | 
			
		||||
        from: from,
 | 
			
		||||
        name: null,
 | 
			
		||||
        dependencies: null,
 | 
			
		||||
        allTransitiveDependencies: null,
 | 
			
		||||
        debugGetCreateSourceHash: null,
 | 
			
		||||
        publisherName: publisherName,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  AutoDisposeFutureProviderElement<List<CustomApp>> createElement() {
 | 
			
		||||
    return _CustomAppsProviderElement(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  bool operator ==(Object other) {
 | 
			
		||||
    return other is CustomAppsProvider && other.publisherName == publisherName;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode {
 | 
			
		||||
    var hash = _SystemHash.combine(0, runtimeType.hashCode);
 | 
			
		||||
    hash = _SystemHash.combine(hash, publisherName.hashCode);
 | 
			
		||||
 | 
			
		||||
    return _SystemHash.finish(hash);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
 | 
			
		||||
// ignore: unused_element
 | 
			
		||||
mixin CustomAppsRef on AutoDisposeFutureProviderRef<List<CustomApp>> {
 | 
			
		||||
  /// The parameter `publisherName` of this provider.
 | 
			
		||||
  String get publisherName;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _CustomAppsProviderElement
 | 
			
		||||
    extends AutoDisposeFutureProviderElement<List<CustomApp>>
 | 
			
		||||
    with CustomAppsRef {
 | 
			
		||||
  _CustomAppsProviderElement(super.provider);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String get publisherName => (origin as CustomAppsProvider).publisherName;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
							
								
								
									
										557
									
								
								lib/screens/developers/edit_app.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										557
									
								
								lib/screens/developers/edit_app.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,557 @@
 | 
			
		||||
import 'package:croppy/croppy.dart' hide cropImage;
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:flutter_hooks/flutter_hooks.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:image_picker/image_picker.dart';
 | 
			
		||||
import 'package:island/models/custom_app.dart';
 | 
			
		||||
import 'package:island/models/file.dart';
 | 
			
		||||
import 'package:island/pods/config.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
import 'package:island/screens/developers/apps.dart';
 | 
			
		||||
import 'package:island/services/file.dart';
 | 
			
		||||
import 'package:island/widgets/alert.dart';
 | 
			
		||||
import 'package:island/widgets/app_scaffold.dart';
 | 
			
		||||
import 'package:island/widgets/content/cloud_files.dart';
 | 
			
		||||
import 'package:island/widgets/response.dart';
 | 
			
		||||
import 'package:material_symbols_icons/symbols.dart';
 | 
			
		||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
			
		||||
import 'package:styled_widget/styled_widget.dart';
 | 
			
		||||
import 'package:island/widgets/content/sheet.dart';
 | 
			
		||||
 | 
			
		||||
part 'edit_app.g.dart';
 | 
			
		||||
 | 
			
		||||
@riverpod
 | 
			
		||||
Future<CustomApp?> customApp(Ref ref, String publisherName, String id) async {
 | 
			
		||||
  final client = ref.watch(apiClientProvider);
 | 
			
		||||
  final resp = await client.get('/developers/$publisherName/apps/$id');
 | 
			
		||||
  return CustomApp.fromJson(resp.data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class EditAppScreen extends HookConsumerWidget {
 | 
			
		||||
  final String publisherName;
 | 
			
		||||
  final String? id;
 | 
			
		||||
  const EditAppScreen({super.key, required this.publisherName, this.id});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    final isNew = id == null;
 | 
			
		||||
    final app = isNew ? null : ref.watch(customAppProvider(publisherName, id!));
 | 
			
		||||
 | 
			
		||||
    final formKey = useMemoized(() => GlobalKey<FormState>());
 | 
			
		||||
 | 
			
		||||
    final submitting = useState(false);
 | 
			
		||||
 | 
			
		||||
    final nameController = useTextEditingController();
 | 
			
		||||
    final slugController = useTextEditingController();
 | 
			
		||||
    final descriptionController = useTextEditingController();
 | 
			
		||||
    final picture = useState<SnCloudFile?>(null);
 | 
			
		||||
    final background = useState<SnCloudFile?>(null);
 | 
			
		||||
 | 
			
		||||
    final enableLinks = useState(false); // Only for UI purposes
 | 
			
		||||
    final homePageController = useTextEditingController();
 | 
			
		||||
    final privacyPolicyController = useTextEditingController();
 | 
			
		||||
    final termsController = useTextEditingController();
 | 
			
		||||
    final oauthEnabled = useState(false);
 | 
			
		||||
    final redirectUris = useState<List<String>>([]);
 | 
			
		||||
    final postLogoutUris = useState<List<String>>([]);
 | 
			
		||||
    final allowedScopes = useState<List<String>>([
 | 
			
		||||
      'openid',
 | 
			
		||||
      'profile',
 | 
			
		||||
      'email',
 | 
			
		||||
    ]);
 | 
			
		||||
    final allowedGrantTypes = useState<List<String>>([
 | 
			
		||||
      'authorization_code',
 | 
			
		||||
      'refresh_token',
 | 
			
		||||
    ]);
 | 
			
		||||
    final requirePkce = useState(true);
 | 
			
		||||
    final allowOfflineAccess = useState(false);
 | 
			
		||||
 | 
			
		||||
    useEffect(() {
 | 
			
		||||
      if (app?.value != null) {
 | 
			
		||||
        nameController.text = app!.value!.name;
 | 
			
		||||
        slugController.text = app.value!.slug;
 | 
			
		||||
        descriptionController.text = app.value!.description ?? '';
 | 
			
		||||
        picture.value = app.value!.picture;
 | 
			
		||||
        background.value = app.value!.background;
 | 
			
		||||
        homePageController.text = app.value!.links?.homePage ?? '';
 | 
			
		||||
        privacyPolicyController.text = app.value!.links?.privacyPolicy ?? '';
 | 
			
		||||
        termsController.text = app.value!.links?.termsOfService ?? '';
 | 
			
		||||
        if (app.value!.oauthConfig != null) {
 | 
			
		||||
          oauthEnabled.value = true;
 | 
			
		||||
          redirectUris.value = app.value!.oauthConfig!.redirectUris;
 | 
			
		||||
          postLogoutUris.value =
 | 
			
		||||
              app.value!.oauthConfig!.postLogoutRedirectUris ?? [];
 | 
			
		||||
          allowedScopes.value = app.value!.oauthConfig!.allowedScopes;
 | 
			
		||||
          allowedGrantTypes.value = app.value!.oauthConfig!.allowedGrantTypes;
 | 
			
		||||
          requirePkce.value = app.value!.oauthConfig!.requirePkce;
 | 
			
		||||
          allowOfflineAccess.value = app.value!.oauthConfig!.allowOfflineAccess;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return null;
 | 
			
		||||
    }, [app]);
 | 
			
		||||
 | 
			
		||||
    void setPicture(String position) async {
 | 
			
		||||
      showLoadingModal(context);
 | 
			
		||||
      var result = await ref
 | 
			
		||||
          .read(imagePickerProvider)
 | 
			
		||||
          .pickImage(source: ImageSource.gallery);
 | 
			
		||||
      if (result == null) {
 | 
			
		||||
        if (context.mounted) hideLoadingModal(context);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      if (!context.mounted) return;
 | 
			
		||||
      hideLoadingModal(context);
 | 
			
		||||
      result = await cropImage(
 | 
			
		||||
        context,
 | 
			
		||||
        image: result,
 | 
			
		||||
        allowedAspectRatios: [
 | 
			
		||||
          if (position == 'background')
 | 
			
		||||
            const CropAspectRatio(height: 7, width: 16)
 | 
			
		||||
          else
 | 
			
		||||
            const CropAspectRatio(height: 1, width: 1),
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
      if (result == null) {
 | 
			
		||||
        if (context.mounted) hideLoadingModal(context);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      if (!context.mounted) return;
 | 
			
		||||
      showLoadingModal(context);
 | 
			
		||||
 | 
			
		||||
      submitting.value = true;
 | 
			
		||||
      try {
 | 
			
		||||
        final baseUrl = ref.watch(serverUrlProvider);
 | 
			
		||||
        final token = await getToken(ref.watch(tokenProvider));
 | 
			
		||||
        if (token == null) throw ArgumentError('Token is null');
 | 
			
		||||
        final cloudFile =
 | 
			
		||||
            await putMediaToCloud(
 | 
			
		||||
              fileData: UniversalFile(
 | 
			
		||||
                data: result,
 | 
			
		||||
                type: UniversalFileType.image,
 | 
			
		||||
              ),
 | 
			
		||||
              atk: token,
 | 
			
		||||
              baseUrl: baseUrl,
 | 
			
		||||
              filename: result.name,
 | 
			
		||||
              mimetype: result.mimeType ?? 'image/jpeg',
 | 
			
		||||
            ).future;
 | 
			
		||||
        if (cloudFile == null) {
 | 
			
		||||
          throw ArgumentError('Failed to upload the file...');
 | 
			
		||||
        }
 | 
			
		||||
        switch (position) {
 | 
			
		||||
          case 'picture':
 | 
			
		||||
            picture.value = cloudFile;
 | 
			
		||||
          case 'background':
 | 
			
		||||
            background.value = cloudFile;
 | 
			
		||||
        }
 | 
			
		||||
      } catch (err) {
 | 
			
		||||
        showErrorAlert(err);
 | 
			
		||||
      } finally {
 | 
			
		||||
        if (context.mounted) hideLoadingModal(context);
 | 
			
		||||
        submitting.value = false;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void showAddScopeDialog() {
 | 
			
		||||
      final scopeController = TextEditingController();
 | 
			
		||||
      showModalBottomSheet(
 | 
			
		||||
        context: context,
 | 
			
		||||
        isScrollControlled: true,
 | 
			
		||||
        builder:
 | 
			
		||||
            (context) => SheetScaffold(
 | 
			
		||||
              titleText: 'addScope'.tr(),
 | 
			
		||||
              child: Padding(
 | 
			
		||||
                padding: const EdgeInsets.all(20),
 | 
			
		||||
                child: Column(
 | 
			
		||||
                  crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
			
		||||
                  children: [
 | 
			
		||||
                    TextFormField(
 | 
			
		||||
                      controller: scopeController,
 | 
			
		||||
                      decoration: InputDecoration(labelText: 'scopeName'.tr()),
 | 
			
		||||
                    ),
 | 
			
		||||
                    const SizedBox(height: 20),
 | 
			
		||||
                    FilledButton.tonalIcon(
 | 
			
		||||
                      onPressed: () {
 | 
			
		||||
                        if (scopeController.text.isNotEmpty) {
 | 
			
		||||
                          allowedScopes.value = [
 | 
			
		||||
                            ...allowedScopes.value,
 | 
			
		||||
                            scopeController.text,
 | 
			
		||||
                          ];
 | 
			
		||||
                          Navigator.pop(context);
 | 
			
		||||
                        }
 | 
			
		||||
                      },
 | 
			
		||||
                      icon: const Icon(Symbols.add),
 | 
			
		||||
                      label: Text('add').tr(),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void showAddRedirectUriDialog() {
 | 
			
		||||
      final uriController = TextEditingController();
 | 
			
		||||
      showModalBottomSheet(
 | 
			
		||||
        context: context,
 | 
			
		||||
        isScrollControlled: true,
 | 
			
		||||
        builder:
 | 
			
		||||
            (context) => SheetScaffold(
 | 
			
		||||
              titleText: 'addRedirectUri'.tr(),
 | 
			
		||||
              child: Padding(
 | 
			
		||||
                padding: const EdgeInsets.all(20),
 | 
			
		||||
                child: Column(
 | 
			
		||||
                  crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
			
		||||
                  children: [
 | 
			
		||||
                    TextFormField(
 | 
			
		||||
                      controller: uriController,
 | 
			
		||||
                      decoration: InputDecoration(
 | 
			
		||||
                        labelText: 'redirectUri'.tr(),
 | 
			
		||||
                        hintText: 'https://example.com/auth/callback',
 | 
			
		||||
                        helperText: 'redirectUriHint'.tr(),
 | 
			
		||||
                        helperMaxLines: 3,
 | 
			
		||||
                      ),
 | 
			
		||||
                      keyboardType: TextInputType.url,
 | 
			
		||||
                      validator: (value) {
 | 
			
		||||
                        if (value == null || value.isEmpty) {
 | 
			
		||||
                          return 'uriRequired'.tr();
 | 
			
		||||
                        }
 | 
			
		||||
                        final uri = Uri.tryParse(value);
 | 
			
		||||
                        if (uri == null || !uri.hasAbsolutePath) {
 | 
			
		||||
                          return 'invalidUri'.tr();
 | 
			
		||||
                        }
 | 
			
		||||
                        return null;
 | 
			
		||||
                      },
 | 
			
		||||
                      onTapOutside:
 | 
			
		||||
                          (_) => FocusManager.instance.primaryFocus?.unfocus(),
 | 
			
		||||
                    ),
 | 
			
		||||
                    const SizedBox(height: 20),
 | 
			
		||||
                    FilledButton.tonalIcon(
 | 
			
		||||
                      onPressed: () {
 | 
			
		||||
                        if (uriController.text.isNotEmpty) {
 | 
			
		||||
                          redirectUris.value = [
 | 
			
		||||
                            ...redirectUris.value,
 | 
			
		||||
                            uriController.text,
 | 
			
		||||
                          ];
 | 
			
		||||
                          Navigator.pop(context);
 | 
			
		||||
                        }
 | 
			
		||||
                      },
 | 
			
		||||
                      icon: const Icon(Symbols.add),
 | 
			
		||||
                      label: Text('add').tr(),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void performAction() async {
 | 
			
		||||
      final client = ref.read(apiClientProvider);
 | 
			
		||||
      final data = {
 | 
			
		||||
        'name': nameController.text,
 | 
			
		||||
        'slug': slugController.text,
 | 
			
		||||
        'description': descriptionController.text,
 | 
			
		||||
        'picture_id': picture.value?.id,
 | 
			
		||||
        'background_id': background.value?.id,
 | 
			
		||||
        'links': {
 | 
			
		||||
          'home_page':
 | 
			
		||||
              homePageController.text.isNotEmpty
 | 
			
		||||
                  ? homePageController.text
 | 
			
		||||
                  : null,
 | 
			
		||||
          'privacy_policy':
 | 
			
		||||
              privacyPolicyController.text.isNotEmpty
 | 
			
		||||
                  ? privacyPolicyController.text
 | 
			
		||||
                  : null,
 | 
			
		||||
          'terms_of_service':
 | 
			
		||||
              termsController.text.isNotEmpty ? termsController.text : null,
 | 
			
		||||
        },
 | 
			
		||||
        'oauth_config':
 | 
			
		||||
            oauthEnabled.value
 | 
			
		||||
                ? {
 | 
			
		||||
                  'redirect_uris': redirectUris.value,
 | 
			
		||||
                  'post_logout_redirect_uris':
 | 
			
		||||
                      postLogoutUris.value.isNotEmpty
 | 
			
		||||
                          ? postLogoutUris.value
 | 
			
		||||
                          : null,
 | 
			
		||||
                  'allowed_scopes': allowedScopes.value,
 | 
			
		||||
                  'allowed_grant_types': allowedGrantTypes.value,
 | 
			
		||||
                  'require_pkce': requirePkce.value,
 | 
			
		||||
                  'allow_offline_access': allowOfflineAccess.value,
 | 
			
		||||
                }
 | 
			
		||||
                : null,
 | 
			
		||||
      };
 | 
			
		||||
      if (isNew) {
 | 
			
		||||
        await client.post('/developers/$publisherName/apps', data: data);
 | 
			
		||||
      } else {
 | 
			
		||||
        await client.patch('/developers/$publisherName/apps/$id', data: data);
 | 
			
		||||
      }
 | 
			
		||||
      ref.invalidate(customAppsProvider(publisherName));
 | 
			
		||||
      if (context.mounted) {
 | 
			
		||||
        Navigator.pop(context);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        title: Text(isNew ? 'createCustomApp'.tr() : 'editCustomApp'.tr()),
 | 
			
		||||
      ),
 | 
			
		||||
      body:
 | 
			
		||||
          app == null && !isNew
 | 
			
		||||
              ? const Center(child: CircularProgressIndicator())
 | 
			
		||||
              : app?.hasError == true && !isNew
 | 
			
		||||
              ? ResponseErrorWidget(
 | 
			
		||||
                error: app!.error,
 | 
			
		||||
                onRetry:
 | 
			
		||||
                    () => ref.invalidate(customAppProvider(publisherName, id!)),
 | 
			
		||||
              )
 | 
			
		||||
              : SingleChildScrollView(
 | 
			
		||||
                child: Column(
 | 
			
		||||
                  children: [
 | 
			
		||||
                    AspectRatio(
 | 
			
		||||
                      aspectRatio: 16 / 7,
 | 
			
		||||
                      child: Stack(
 | 
			
		||||
                        clipBehavior: Clip.none,
 | 
			
		||||
                        fit: StackFit.expand,
 | 
			
		||||
                        children: [
 | 
			
		||||
                          GestureDetector(
 | 
			
		||||
                            child: Container(
 | 
			
		||||
                              color:
 | 
			
		||||
                                  Theme.of(
 | 
			
		||||
                                    context,
 | 
			
		||||
                                  ).colorScheme.surfaceContainerHigh,
 | 
			
		||||
                              child:
 | 
			
		||||
                                  background.value != null
 | 
			
		||||
                                      ? CloudFileWidget(
 | 
			
		||||
                                        item: background.value!,
 | 
			
		||||
                                        fit: BoxFit.cover,
 | 
			
		||||
                                      )
 | 
			
		||||
                                      : const SizedBox.shrink(),
 | 
			
		||||
                            ),
 | 
			
		||||
                            onTap: () {
 | 
			
		||||
                              setPicture('background');
 | 
			
		||||
                            },
 | 
			
		||||
                          ),
 | 
			
		||||
                          Positioned(
 | 
			
		||||
                            left: 20,
 | 
			
		||||
                            bottom: -32,
 | 
			
		||||
                            child: GestureDetector(
 | 
			
		||||
                              child: ProfilePictureWidget(
 | 
			
		||||
                                fileId: picture.value?.id,
 | 
			
		||||
                                radius: 40,
 | 
			
		||||
                                fallbackIcon: Symbols.apps,
 | 
			
		||||
                              ),
 | 
			
		||||
                              onTap: () {
 | 
			
		||||
                                setPicture('picture');
 | 
			
		||||
                              },
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ),
 | 
			
		||||
                    ).padding(bottom: 32),
 | 
			
		||||
                    Form(
 | 
			
		||||
                      key: formKey,
 | 
			
		||||
                      child: Column(
 | 
			
		||||
                        children: [
 | 
			
		||||
                          TextFormField(
 | 
			
		||||
                            controller: nameController,
 | 
			
		||||
                            decoration: InputDecoration(labelText: 'name'.tr()),
 | 
			
		||||
                            onTapOutside:
 | 
			
		||||
                                (_) =>
 | 
			
		||||
                                    FocusManager.instance.primaryFocus
 | 
			
		||||
                                        ?.unfocus(),
 | 
			
		||||
                          ),
 | 
			
		||||
                          const SizedBox(height: 16),
 | 
			
		||||
                          TextFormField(
 | 
			
		||||
                            controller: slugController,
 | 
			
		||||
                            decoration: InputDecoration(
 | 
			
		||||
                              labelText: 'slug'.tr(),
 | 
			
		||||
                              helperText: 'slugHint'.tr(),
 | 
			
		||||
                            ),
 | 
			
		||||
                            onTapOutside:
 | 
			
		||||
                                (_) =>
 | 
			
		||||
                                    FocusManager.instance.primaryFocus
 | 
			
		||||
                                        ?.unfocus(),
 | 
			
		||||
                          ),
 | 
			
		||||
                          const SizedBox(height: 16),
 | 
			
		||||
                          TextFormField(
 | 
			
		||||
                            controller: descriptionController,
 | 
			
		||||
                            decoration: InputDecoration(
 | 
			
		||||
                              labelText: 'description'.tr(),
 | 
			
		||||
                            ),
 | 
			
		||||
                            maxLines: 3,
 | 
			
		||||
                            onTapOutside:
 | 
			
		||||
                                (_) =>
 | 
			
		||||
                                    FocusManager.instance.primaryFocus
 | 
			
		||||
                                        ?.unfocus(),
 | 
			
		||||
                          ),
 | 
			
		||||
                          const SizedBox(height: 16),
 | 
			
		||||
                          ExpansionPanelList(
 | 
			
		||||
                            expansionCallback: (index, isExpanded) {
 | 
			
		||||
                              switch (index) {
 | 
			
		||||
                                case 0:
 | 
			
		||||
                                  enableLinks.value = isExpanded;
 | 
			
		||||
                                  break;
 | 
			
		||||
                                case 1:
 | 
			
		||||
                                  oauthEnabled.value = isExpanded;
 | 
			
		||||
                                  break;
 | 
			
		||||
                              }
 | 
			
		||||
                            },
 | 
			
		||||
                            children: [
 | 
			
		||||
                              ExpansionPanel(
 | 
			
		||||
                                headerBuilder:
 | 
			
		||||
                                    (context, isExpanded) =>
 | 
			
		||||
                                        ListTile(title: Text('appLinks').tr()),
 | 
			
		||||
                                body: Column(
 | 
			
		||||
                                  spacing: 16,
 | 
			
		||||
                                  children: [
 | 
			
		||||
                                    TextFormField(
 | 
			
		||||
                                      controller: homePageController,
 | 
			
		||||
                                      decoration: InputDecoration(
 | 
			
		||||
                                        labelText: 'homePageUrl'.tr(),
 | 
			
		||||
                                        hintText: 'https://example.com',
 | 
			
		||||
                                      ),
 | 
			
		||||
                                      keyboardType: TextInputType.url,
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    TextFormField(
 | 
			
		||||
                                      controller: privacyPolicyController,
 | 
			
		||||
                                      decoration: InputDecoration(
 | 
			
		||||
                                        labelText: 'privacyPolicyUrl'.tr(),
 | 
			
		||||
                                        hintText: 'https://example.com/privacy',
 | 
			
		||||
                                      ),
 | 
			
		||||
                                      keyboardType: TextInputType.url,
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    TextFormField(
 | 
			
		||||
                                      controller: termsController,
 | 
			
		||||
                                      decoration: InputDecoration(
 | 
			
		||||
                                        labelText: 'termsOfServiceUrl'.tr(),
 | 
			
		||||
                                        hintText: 'https://example.com/terms',
 | 
			
		||||
                                      ),
 | 
			
		||||
                                      keyboardType: TextInputType.url,
 | 
			
		||||
                                    ),
 | 
			
		||||
                                  ],
 | 
			
		||||
                                ).padding(horizontal: 16, bottom: 24),
 | 
			
		||||
                                isExpanded: enableLinks.value,
 | 
			
		||||
                              ),
 | 
			
		||||
                              ExpansionPanel(
 | 
			
		||||
                                headerBuilder:
 | 
			
		||||
                                    (context, isExpanded) => ListTile(
 | 
			
		||||
                                      title: Text('oauthConfig').tr(),
 | 
			
		||||
                                    ),
 | 
			
		||||
                                body: Column(
 | 
			
		||||
                                  crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                                  children: [
 | 
			
		||||
                                    Text('redirectUris'.tr()),
 | 
			
		||||
                                    Card(
 | 
			
		||||
                                      margin: const EdgeInsets.symmetric(
 | 
			
		||||
                                        vertical: 8,
 | 
			
		||||
                                      ),
 | 
			
		||||
                                      child: Column(
 | 
			
		||||
                                        children: [
 | 
			
		||||
                                          ...redirectUris.value.map(
 | 
			
		||||
                                            (uri) => ListTile(
 | 
			
		||||
                                              title: Text(uri),
 | 
			
		||||
                                              trailing: IconButton(
 | 
			
		||||
                                                icon: const Icon(
 | 
			
		||||
                                                  Symbols.delete,
 | 
			
		||||
                                                ),
 | 
			
		||||
                                                onPressed: () {
 | 
			
		||||
                                                  redirectUris.value =
 | 
			
		||||
                                                      redirectUris.value
 | 
			
		||||
                                                          .where(
 | 
			
		||||
                                                            (u) => u != uri,
 | 
			
		||||
                                                          )
 | 
			
		||||
                                                          .toList();
 | 
			
		||||
                                                },
 | 
			
		||||
                                              ),
 | 
			
		||||
                                            ),
 | 
			
		||||
                                          ),
 | 
			
		||||
                                          if (redirectUris.value.isNotEmpty)
 | 
			
		||||
                                            const Divider(height: 1),
 | 
			
		||||
                                          ListTile(
 | 
			
		||||
                                            leading: const Icon(Symbols.add),
 | 
			
		||||
                                            title: Text('addRedirectUri'.tr()),
 | 
			
		||||
                                            onTap: showAddRedirectUriDialog,
 | 
			
		||||
                                            shape: RoundedRectangleBorder(
 | 
			
		||||
                                              borderRadius:
 | 
			
		||||
                                                  BorderRadius.circular(8),
 | 
			
		||||
                                            ),
 | 
			
		||||
                                          ),
 | 
			
		||||
                                        ],
 | 
			
		||||
                                      ),
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    const SizedBox(height: 16),
 | 
			
		||||
                                    Text('allowedScopes'.tr()),
 | 
			
		||||
                                    Card(
 | 
			
		||||
                                      margin: const EdgeInsets.symmetric(
 | 
			
		||||
                                        vertical: 8,
 | 
			
		||||
                                      ),
 | 
			
		||||
                                      child: Column(
 | 
			
		||||
                                        children: [
 | 
			
		||||
                                          ...allowedScopes.value.map(
 | 
			
		||||
                                            (scope) => ListTile(
 | 
			
		||||
                                              title: Text(scope),
 | 
			
		||||
                                              trailing: IconButton(
 | 
			
		||||
                                                icon: const Icon(
 | 
			
		||||
                                                  Symbols.delete,
 | 
			
		||||
                                                ),
 | 
			
		||||
                                                onPressed: () {
 | 
			
		||||
                                                  allowedScopes.value =
 | 
			
		||||
                                                      allowedScopes.value
 | 
			
		||||
                                                          .where(
 | 
			
		||||
                                                            (s) => s != scope,
 | 
			
		||||
                                                          )
 | 
			
		||||
                                                          .toList();
 | 
			
		||||
                                                },
 | 
			
		||||
                                              ),
 | 
			
		||||
                                            ),
 | 
			
		||||
                                          ),
 | 
			
		||||
                                          if (allowedScopes.value.isNotEmpty)
 | 
			
		||||
                                            const Divider(height: 1),
 | 
			
		||||
                                          ListTile(
 | 
			
		||||
                                            leading: const Icon(Symbols.add),
 | 
			
		||||
                                            title: Text('add').tr(),
 | 
			
		||||
                                            onTap: showAddScopeDialog,
 | 
			
		||||
                                          ),
 | 
			
		||||
                                        ],
 | 
			
		||||
                                      ),
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    const SizedBox(height: 16),
 | 
			
		||||
                                    SwitchListTile(
 | 
			
		||||
                                      title: Text('requirePkce'.tr()),
 | 
			
		||||
                                      value: requirePkce.value,
 | 
			
		||||
                                      onChanged:
 | 
			
		||||
                                          (value) => requirePkce.value = value,
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    SwitchListTile(
 | 
			
		||||
                                      title: Text('allowOfflineAccess'.tr()),
 | 
			
		||||
                                      value: allowOfflineAccess.value,
 | 
			
		||||
                                      onChanged:
 | 
			
		||||
                                          (value) =>
 | 
			
		||||
                                              allowOfflineAccess.value = value,
 | 
			
		||||
                                    ),
 | 
			
		||||
                                  ],
 | 
			
		||||
                                ).padding(horizontal: 16, bottom: 24),
 | 
			
		||||
                                isExpanded: oauthEnabled.value,
 | 
			
		||||
                              ),
 | 
			
		||||
                            ],
 | 
			
		||||
                          ),
 | 
			
		||||
                          const SizedBox(height: 16),
 | 
			
		||||
                          Align(
 | 
			
		||||
                            alignment: Alignment.centerRight,
 | 
			
		||||
                            child: TextButton.icon(
 | 
			
		||||
                              onPressed:
 | 
			
		||||
                                  submitting.value ? null : performAction,
 | 
			
		||||
                              label: Text('saveChanges'.tr()),
 | 
			
		||||
                              icon: const Icon(Symbols.save),
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ).padding(all: 24),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										161
									
								
								lib/screens/developers/edit_app.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								lib/screens/developers/edit_app.g.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,161 @@
 | 
			
		||||
// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
			
		||||
 | 
			
		||||
part of 'edit_app.dart';
 | 
			
		||||
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
// RiverpodGenerator
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
 | 
			
		||||
String _$customAppHash() => r'aa4d1fb803c47a99cbacf6d91481f4fce3fda457';
 | 
			
		||||
 | 
			
		||||
/// 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 [customApp].
 | 
			
		||||
@ProviderFor(customApp)
 | 
			
		||||
const customAppProvider = CustomAppFamily();
 | 
			
		||||
 | 
			
		||||
/// See also [customApp].
 | 
			
		||||
class CustomAppFamily extends Family<AsyncValue<CustomApp?>> {
 | 
			
		||||
  /// See also [customApp].
 | 
			
		||||
  const CustomAppFamily();
 | 
			
		||||
 | 
			
		||||
  /// See also [customApp].
 | 
			
		||||
  CustomAppProvider call(String publisherName, String id) {
 | 
			
		||||
    return CustomAppProvider(publisherName, id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  CustomAppProvider getProviderOverride(covariant CustomAppProvider provider) {
 | 
			
		||||
    return call(provider.publisherName, provider.id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static const Iterable<ProviderOrFamily>? _dependencies = null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Iterable<ProviderOrFamily>? get dependencies => _dependencies;
 | 
			
		||||
 | 
			
		||||
  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
 | 
			
		||||
      _allTransitiveDependencies;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String? get name => r'customAppProvider';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// See also [customApp].
 | 
			
		||||
class CustomAppProvider extends AutoDisposeFutureProvider<CustomApp?> {
 | 
			
		||||
  /// See also [customApp].
 | 
			
		||||
  CustomAppProvider(String publisherName, String id)
 | 
			
		||||
    : this._internal(
 | 
			
		||||
        (ref) => customApp(ref as CustomAppRef, publisherName, id),
 | 
			
		||||
        from: customAppProvider,
 | 
			
		||||
        name: r'customAppProvider',
 | 
			
		||||
        debugGetCreateSourceHash:
 | 
			
		||||
            const bool.fromEnvironment('dart.vm.product')
 | 
			
		||||
                ? null
 | 
			
		||||
                : _$customAppHash,
 | 
			
		||||
        dependencies: CustomAppFamily._dependencies,
 | 
			
		||||
        allTransitiveDependencies: CustomAppFamily._allTransitiveDependencies,
 | 
			
		||||
        publisherName: publisherName,
 | 
			
		||||
        id: id,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  CustomAppProvider._internal(
 | 
			
		||||
    super._createNotifier, {
 | 
			
		||||
    required super.name,
 | 
			
		||||
    required super.dependencies,
 | 
			
		||||
    required super.allTransitiveDependencies,
 | 
			
		||||
    required super.debugGetCreateSourceHash,
 | 
			
		||||
    required super.from,
 | 
			
		||||
    required this.publisherName,
 | 
			
		||||
    required this.id,
 | 
			
		||||
  }) : super.internal();
 | 
			
		||||
 | 
			
		||||
  final String publisherName;
 | 
			
		||||
  final String id;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Override overrideWith(
 | 
			
		||||
    FutureOr<CustomApp?> Function(CustomAppRef provider) create,
 | 
			
		||||
  ) {
 | 
			
		||||
    return ProviderOverride(
 | 
			
		||||
      origin: this,
 | 
			
		||||
      override: CustomAppProvider._internal(
 | 
			
		||||
        (ref) => create(ref as CustomAppRef),
 | 
			
		||||
        from: from,
 | 
			
		||||
        name: null,
 | 
			
		||||
        dependencies: null,
 | 
			
		||||
        allTransitiveDependencies: null,
 | 
			
		||||
        debugGetCreateSourceHash: null,
 | 
			
		||||
        publisherName: publisherName,
 | 
			
		||||
        id: id,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  AutoDisposeFutureProviderElement<CustomApp?> createElement() {
 | 
			
		||||
    return _CustomAppProviderElement(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  bool operator ==(Object other) {
 | 
			
		||||
    return other is CustomAppProvider &&
 | 
			
		||||
        other.publisherName == publisherName &&
 | 
			
		||||
        other.id == id;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode {
 | 
			
		||||
    var hash = _SystemHash.combine(0, runtimeType.hashCode);
 | 
			
		||||
    hash = _SystemHash.combine(hash, publisherName.hashCode);
 | 
			
		||||
    hash = _SystemHash.combine(hash, id.hashCode);
 | 
			
		||||
 | 
			
		||||
    return _SystemHash.finish(hash);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
 | 
			
		||||
// ignore: unused_element
 | 
			
		||||
mixin CustomAppRef on AutoDisposeFutureProviderRef<CustomApp?> {
 | 
			
		||||
  /// The parameter `publisherName` of this provider.
 | 
			
		||||
  String get publisherName;
 | 
			
		||||
 | 
			
		||||
  /// The parameter `id` of this provider.
 | 
			
		||||
  String get id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _CustomAppProviderElement
 | 
			
		||||
    extends AutoDisposeFutureProviderElement<CustomApp?>
 | 
			
		||||
    with CustomAppRef {
 | 
			
		||||
  _CustomAppProviderElement(super.provider);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String get publisherName => (origin as CustomAppProvider).publisherName;
 | 
			
		||||
  @override
 | 
			
		||||
  String get id => (origin as CustomAppProvider).id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ignore_for_file: type=lint
 | 
			
		||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
 | 
			
		||||
							
								
								
									
										380
									
								
								lib/screens/developers/hub.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										380
									
								
								lib/screens/developers/hub.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,380 @@
 | 
			
		||||
import 'package:dropdown_button2/dropdown_button2.dart';
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_hooks/flutter_hooks.dart';
 | 
			
		||||
import 'package:gap/gap.dart';
 | 
			
		||||
import 'package:go_router/go_router.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/developer.dart';
 | 
			
		||||
import 'package:island/models/publisher.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
import 'package:island/screens/creators/publishers.dart';
 | 
			
		||||
import 'package:island/services/responsive.dart';
 | 
			
		||||
import 'package:island/widgets/alert.dart';
 | 
			
		||||
import 'package:island/widgets/app_scaffold.dart';
 | 
			
		||||
import 'package:island/widgets/content/cloud_files.dart';
 | 
			
		||||
import 'package:island/widgets/content/sheet.dart';
 | 
			
		||||
import 'package:island/widgets/response.dart';
 | 
			
		||||
import 'package:material_symbols_icons/symbols.dart';
 | 
			
		||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
			
		||||
import 'package:styled_widget/styled_widget.dart';
 | 
			
		||||
 | 
			
		||||
part 'hub.g.dart';
 | 
			
		||||
 | 
			
		||||
@riverpod
 | 
			
		||||
Future<DeveloperStats?> developerStats(Ref ref, String? uname) async {
 | 
			
		||||
  if (uname == null) return null;
 | 
			
		||||
  final apiClient = ref.watch(apiClientProvider);
 | 
			
		||||
  final resp = await apiClient.get('/developers/$uname/stats');
 | 
			
		||||
  return DeveloperStats.fromJson(resp.data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@riverpod
 | 
			
		||||
Future<List<SnPublisher>> developers(Ref ref) async {
 | 
			
		||||
  final client = ref.watch(apiClientProvider);
 | 
			
		||||
  final resp = await client.get('/developers');
 | 
			
		||||
  return resp.data
 | 
			
		||||
      .map((e) => SnPublisher.fromJson(e))
 | 
			
		||||
      .cast<SnPublisher>()
 | 
			
		||||
      .toList();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DeveloperHubShellScreen extends StatelessWidget {
 | 
			
		||||
  final Widget child;
 | 
			
		||||
  const DeveloperHubShellScreen({super.key, required this.child});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final isWide = isWideScreen(context);
 | 
			
		||||
    if (isWide) {
 | 
			
		||||
      return Row(
 | 
			
		||||
        children: [
 | 
			
		||||
          SizedBox(width: 360, child: const DeveloperHubScreen(isAside: true)),
 | 
			
		||||
          const VerticalDivider(width: 1),
 | 
			
		||||
          Expanded(child: child),
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    return child;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DeveloperHubScreen extends HookConsumerWidget {
 | 
			
		||||
  final bool isAside;
 | 
			
		||||
  const DeveloperHubScreen({super.key, this.isAside = false});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    final isWide = isWideScreen(context);
 | 
			
		||||
    if (isWide && !isAside) {
 | 
			
		||||
      return Container(color: Theme.of(context).colorScheme.surface);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final developers = ref.watch(developersProvider);
 | 
			
		||||
    final currentDeveloper = useState<SnPublisher?>(
 | 
			
		||||
      developers.value?.firstOrNull,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    final List<DropdownMenuItem<SnPublisher>> developersMenu = developers.when(
 | 
			
		||||
      data:
 | 
			
		||||
          (data) =>
 | 
			
		||||
              data
 | 
			
		||||
                  .map(
 | 
			
		||||
                    (item) => DropdownMenuItem<SnPublisher>(
 | 
			
		||||
                      value: item,
 | 
			
		||||
                      child: ListTile(
 | 
			
		||||
                        minTileHeight: 48,
 | 
			
		||||
                        leading: ProfilePictureWidget(
 | 
			
		||||
                          radius: 16,
 | 
			
		||||
                          fileId: item.picture?.id,
 | 
			
		||||
                        ),
 | 
			
		||||
                        title: Text(item.nick),
 | 
			
		||||
                        subtitle: Text('@${item.name}'),
 | 
			
		||||
                        trailing:
 | 
			
		||||
                            currentDeveloper.value?.id == item.id
 | 
			
		||||
                                ? const Icon(Icons.check)
 | 
			
		||||
                                : null,
 | 
			
		||||
                        contentPadding: EdgeInsets.symmetric(horizontal: 8),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                  )
 | 
			
		||||
                  .toList(),
 | 
			
		||||
      loading: () => [],
 | 
			
		||||
      error: (_, _) => [],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    final developerStats = ref.watch(
 | 
			
		||||
      developerStatsProvider(currentDeveloper.value?.name),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      noBackground: false,
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: !isWide ? const PageBackButton() : null,
 | 
			
		||||
        title: Text('developerHub').tr(),
 | 
			
		||||
        actions: [
 | 
			
		||||
          DropdownButtonHideUnderline(
 | 
			
		||||
            child: DropdownButton2<SnPublisher>(
 | 
			
		||||
              alignment: Alignment.centerRight,
 | 
			
		||||
              value: currentDeveloper.value,
 | 
			
		||||
              hint: CircleAvatar(
 | 
			
		||||
                radius: 16,
 | 
			
		||||
                child: Icon(
 | 
			
		||||
                  Symbols.person,
 | 
			
		||||
                  color: Theme.of(
 | 
			
		||||
                    context,
 | 
			
		||||
                  ).colorScheme.onSecondaryContainer.withOpacity(0.9),
 | 
			
		||||
                  fill: 1,
 | 
			
		||||
                ),
 | 
			
		||||
              ).center().padding(right: 8),
 | 
			
		||||
              items: [...developersMenu],
 | 
			
		||||
              onChanged: (value) {
 | 
			
		||||
                currentDeveloper.value = value;
 | 
			
		||||
              },
 | 
			
		||||
              selectedItemBuilder: (context) {
 | 
			
		||||
                return [
 | 
			
		||||
                  ...developersMenu.map(
 | 
			
		||||
                    (e) => ProfilePictureWidget(
 | 
			
		||||
                      radius: 16,
 | 
			
		||||
                      fileId: e.value?.picture?.id,
 | 
			
		||||
                    ).center().padding(right: 8),
 | 
			
		||||
                  ),
 | 
			
		||||
                ];
 | 
			
		||||
              },
 | 
			
		||||
              buttonStyleData: ButtonStyleData(
 | 
			
		||||
                height: 40,
 | 
			
		||||
                padding: const EdgeInsets.only(left: 14, right: 8),
 | 
			
		||||
                decoration: BoxDecoration(
 | 
			
		||||
                  borderRadius: BorderRadius.circular(20),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              dropdownStyleData: DropdownStyleData(
 | 
			
		||||
                width: 320,
 | 
			
		||||
                padding: const EdgeInsets.symmetric(vertical: 6),
 | 
			
		||||
                decoration: BoxDecoration(
 | 
			
		||||
                  borderRadius: BorderRadius.circular(4),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              menuItemStyleData: const MenuItemStyleData(
 | 
			
		||||
                height: 64,
 | 
			
		||||
                padding: EdgeInsets.only(left: 14, right: 14),
 | 
			
		||||
              ),
 | 
			
		||||
              iconStyleData: IconStyleData(
 | 
			
		||||
                icon: Icon(Icons.arrow_drop_down),
 | 
			
		||||
                iconSize: 19,
 | 
			
		||||
                iconEnabledColor:
 | 
			
		||||
                    Theme.of(context).appBarTheme.foregroundColor!,
 | 
			
		||||
                iconDisabledColor:
 | 
			
		||||
                    Theme.of(context).appBarTheme.foregroundColor!,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          const Gap(8),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
      body: developerStats.when(
 | 
			
		||||
        data:
 | 
			
		||||
            (stats) => SingleChildScrollView(
 | 
			
		||||
              child:
 | 
			
		||||
                  currentDeveloper.value == null
 | 
			
		||||
                      ? Column(
 | 
			
		||||
                        children: [
 | 
			
		||||
                          const Gap(24),
 | 
			
		||||
                          const Icon(Symbols.info, size: 32).padding(bottom: 4),
 | 
			
		||||
                          Text(
 | 
			
		||||
                            'developerHubUnselectedHint',
 | 
			
		||||
                            textAlign: TextAlign.center,
 | 
			
		||||
                          ).tr(),
 | 
			
		||||
                          const Gap(24),
 | 
			
		||||
                          const Divider(height: 1),
 | 
			
		||||
                          ...(developers.value?.map(
 | 
			
		||||
                                (developer) => ListTile(
 | 
			
		||||
                                  leading: ProfilePictureWidget(
 | 
			
		||||
                                    file: developer.picture,
 | 
			
		||||
                                  ),
 | 
			
		||||
                                  title: Text(developer.nick),
 | 
			
		||||
                                  subtitle: Text('@${developer.name}'),
 | 
			
		||||
                                  onTap: () {
 | 
			
		||||
                                    currentDeveloper.value = developer;
 | 
			
		||||
                                  },
 | 
			
		||||
                                ),
 | 
			
		||||
                              ) ??
 | 
			
		||||
                              []),
 | 
			
		||||
                          ListTile(
 | 
			
		||||
                            leading: const CircleAvatar(
 | 
			
		||||
                              child: Icon(Symbols.add),
 | 
			
		||||
                            ),
 | 
			
		||||
                            title: Text('enrollDeveloper').tr(),
 | 
			
		||||
                            subtitle: Text('enrollDeveloperHint').tr(),
 | 
			
		||||
                            trailing: const Icon(Symbols.chevron_right),
 | 
			
		||||
                            onTap: () {
 | 
			
		||||
                              showModalBottomSheet(
 | 
			
		||||
                                context: context,
 | 
			
		||||
                                isScrollControlled: true,
 | 
			
		||||
                                builder:
 | 
			
		||||
                                    (_) => const _DeveloperEnrollmentSheet(),
 | 
			
		||||
                              ).then((value) {
 | 
			
		||||
                                if (value == true) {
 | 
			
		||||
                                  ref.invalidate(developersProvider);
 | 
			
		||||
                                }
 | 
			
		||||
                              });
 | 
			
		||||
                            },
 | 
			
		||||
                          ),
 | 
			
		||||
                        ],
 | 
			
		||||
                      )
 | 
			
		||||
                      : Column(
 | 
			
		||||
                        children: [
 | 
			
		||||
                          if (stats != null)
 | 
			
		||||
                            _DeveloperStatsWidget(
 | 
			
		||||
                              stats: stats,
 | 
			
		||||
                            ).padding(vertical: 12, horizontal: 12),
 | 
			
		||||
                          ListTile(
 | 
			
		||||
                            minTileHeight: 48,
 | 
			
		||||
                            title: Text('customApps').tr(),
 | 
			
		||||
                            trailing: Icon(Symbols.chevron_right),
 | 
			
		||||
                            leading: const Icon(Symbols.apps),
 | 
			
		||||
                            contentPadding: EdgeInsets.symmetric(
 | 
			
		||||
                              horizontal: 24,
 | 
			
		||||
                            ),
 | 
			
		||||
                            onTap: () {
 | 
			
		||||
                              context.push(
 | 
			
		||||
                          '/developers/${currentDeveloper.value!.name}/apps',
 | 
			
		||||
                        );
 | 
			
		||||
                            },
 | 
			
		||||
                          ),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ),
 | 
			
		||||
            ),
 | 
			
		||||
        loading: () => const Center(child: CircularProgressIndicator()),
 | 
			
		||||
        error:
 | 
			
		||||
            (err, stack) => ResponseErrorWidget(
 | 
			
		||||
              error: err,
 | 
			
		||||
              onRetry: () {
 | 
			
		||||
                ref.invalidate(
 | 
			
		||||
                  developerStatsProvider(currentDeveloper.value?.name),
 | 
			
		||||
                );
 | 
			
		||||
              },
 | 
			
		||||
            ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _DeveloperStatsWidget extends StatelessWidget {
 | 
			
		||||
  final DeveloperStats stats;
 | 
			
		||||
  const _DeveloperStatsWidget({required this.stats});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return SingleChildScrollView(
 | 
			
		||||
      child: Column(
 | 
			
		||||
        spacing: 8,
 | 
			
		||||
        children: [
 | 
			
		||||
          Row(
 | 
			
		||||
            spacing: 8,
 | 
			
		||||
            children: [
 | 
			
		||||
              Expanded(
 | 
			
		||||
                child: _buildStatsCard(
 | 
			
		||||
                  context,
 | 
			
		||||
                  stats.totalCustomApps.toString(),
 | 
			
		||||
                  'totalCustomApps',
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget _buildStatsCard(
 | 
			
		||||
    BuildContext context,
 | 
			
		||||
    String statValue,
 | 
			
		||||
    String statLabel,
 | 
			
		||||
  ) {
 | 
			
		||||
    return Card(
 | 
			
		||||
      margin: EdgeInsets.zero,
 | 
			
		||||
      child: SizedBox(
 | 
			
		||||
        height: 100,
 | 
			
		||||
        child: Padding(
 | 
			
		||||
          padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
 | 
			
		||||
          child: Column(
 | 
			
		||||
            crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
			
		||||
            mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
            children: [
 | 
			
		||||
              Text(
 | 
			
		||||
                statValue,
 | 
			
		||||
                style: Theme.of(context).textTheme.headlineMedium,
 | 
			
		||||
              ),
 | 
			
		||||
              const Gap(4),
 | 
			
		||||
              Text(
 | 
			
		||||
                statLabel,
 | 
			
		||||
                maxLines: 1,
 | 
			
		||||
                overflow: TextOverflow.ellipsis,
 | 
			
		||||
              ).tr(),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _DeveloperEnrollmentSheet extends HookConsumerWidget {
 | 
			
		||||
  const _DeveloperEnrollmentSheet();
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    final publishers = ref.watch(publishersManagedProvider);
 | 
			
		||||
 | 
			
		||||
    Future<void> enroll(SnPublisher publisher) async {
 | 
			
		||||
      try {
 | 
			
		||||
        final client = ref.read(apiClientProvider);
 | 
			
		||||
        await client.post('/developers/${publisher.name}/enroll');
 | 
			
		||||
        if (context.mounted) {
 | 
			
		||||
          Navigator.pop(context, true);
 | 
			
		||||
        }
 | 
			
		||||
      } catch (err) {
 | 
			
		||||
        showErrorAlert(err);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return SheetScaffold(
 | 
			
		||||
      titleText: 'enrollDeveloper'.tr(),
 | 
			
		||||
      child: publishers.when(
 | 
			
		||||
        data:
 | 
			
		||||
            (items) =>
 | 
			
		||||
                items.isEmpty
 | 
			
		||||
                    ? Center(
 | 
			
		||||
                      child:
 | 
			
		||||
                          Text(
 | 
			
		||||
                            'noPublishersToEnroll',
 | 
			
		||||
                            textAlign: TextAlign.center,
 | 
			
		||||
                          ).tr(),
 | 
			
		||||
                    )
 | 
			
		||||
                    : ListView.builder(
 | 
			
		||||
                      shrinkWrap: true,
 | 
			
		||||
                      itemCount: items.length,
 | 
			
		||||
                      itemBuilder: (context, index) {
 | 
			
		||||
                        final publisher = items[index];
 | 
			
		||||
                        return ListTile(
 | 
			
		||||
                          leading: ProfilePictureWidget(
 | 
			
		||||
                            fileId: publisher.picture?.id,
 | 
			
		||||
                            fallbackIcon: Symbols.group,
 | 
			
		||||
                          ),
 | 
			
		||||
                          title: Text(publisher.nick),
 | 
			
		||||
                          subtitle: Text('@${publisher.name}'),
 | 
			
		||||
                          onTap: () => enroll(publisher),
 | 
			
		||||
                        );
 | 
			
		||||
                      },
 | 
			
		||||
                    ),
 | 
			
		||||
        loading: () => const Center(child: CircularProgressIndicator()),
 | 
			
		||||
        error:
 | 
			
		||||
            (error, _) => ResponseErrorWidget(
 | 
			
		||||
              error: error,
 | 
			
		||||
              onRetry: () => ref.invalidate(publishersManagedProvider),
 | 
			
		||||
            ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										172
									
								
								lib/screens/developers/hub.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								lib/screens/developers/hub.g.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,172 @@
 | 
			
		||||
// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
			
		||||
 | 
			
		||||
part of 'hub.dart';
 | 
			
		||||
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
// RiverpodGenerator
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
 | 
			
		||||
String _$developerStatsHash() => r'783398cbde09c3d956c3e20b02a1cebd1f8ab748';
 | 
			
		||||
 | 
			
		||||
/// 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 [developerStats].
 | 
			
		||||
@ProviderFor(developerStats)
 | 
			
		||||
const developerStatsProvider = DeveloperStatsFamily();
 | 
			
		||||
 | 
			
		||||
/// See also [developerStats].
 | 
			
		||||
class DeveloperStatsFamily extends Family<AsyncValue<DeveloperStats?>> {
 | 
			
		||||
  /// See also [developerStats].
 | 
			
		||||
  const DeveloperStatsFamily();
 | 
			
		||||
 | 
			
		||||
  /// See also [developerStats].
 | 
			
		||||
  DeveloperStatsProvider call(String? uname) {
 | 
			
		||||
    return DeveloperStatsProvider(uname);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  DeveloperStatsProvider getProviderOverride(
 | 
			
		||||
    covariant DeveloperStatsProvider provider,
 | 
			
		||||
  ) {
 | 
			
		||||
    return call(provider.uname);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static const Iterable<ProviderOrFamily>? _dependencies = null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Iterable<ProviderOrFamily>? get dependencies => _dependencies;
 | 
			
		||||
 | 
			
		||||
  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
 | 
			
		||||
      _allTransitiveDependencies;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String? get name => r'developerStatsProvider';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// See also [developerStats].
 | 
			
		||||
class DeveloperStatsProvider
 | 
			
		||||
    extends AutoDisposeFutureProvider<DeveloperStats?> {
 | 
			
		||||
  /// See also [developerStats].
 | 
			
		||||
  DeveloperStatsProvider(String? uname)
 | 
			
		||||
    : this._internal(
 | 
			
		||||
        (ref) => developerStats(ref as DeveloperStatsRef, uname),
 | 
			
		||||
        from: developerStatsProvider,
 | 
			
		||||
        name: r'developerStatsProvider',
 | 
			
		||||
        debugGetCreateSourceHash:
 | 
			
		||||
            const bool.fromEnvironment('dart.vm.product')
 | 
			
		||||
                ? null
 | 
			
		||||
                : _$developerStatsHash,
 | 
			
		||||
        dependencies: DeveloperStatsFamily._dependencies,
 | 
			
		||||
        allTransitiveDependencies:
 | 
			
		||||
            DeveloperStatsFamily._allTransitiveDependencies,
 | 
			
		||||
        uname: uname,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
  DeveloperStatsProvider._internal(
 | 
			
		||||
    super._createNotifier, {
 | 
			
		||||
    required super.name,
 | 
			
		||||
    required super.dependencies,
 | 
			
		||||
    required super.allTransitiveDependencies,
 | 
			
		||||
    required super.debugGetCreateSourceHash,
 | 
			
		||||
    required super.from,
 | 
			
		||||
    required this.uname,
 | 
			
		||||
  }) : super.internal();
 | 
			
		||||
 | 
			
		||||
  final String? uname;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Override overrideWith(
 | 
			
		||||
    FutureOr<DeveloperStats?> Function(DeveloperStatsRef provider) create,
 | 
			
		||||
  ) {
 | 
			
		||||
    return ProviderOverride(
 | 
			
		||||
      origin: this,
 | 
			
		||||
      override: DeveloperStatsProvider._internal(
 | 
			
		||||
        (ref) => create(ref as DeveloperStatsRef),
 | 
			
		||||
        from: from,
 | 
			
		||||
        name: null,
 | 
			
		||||
        dependencies: null,
 | 
			
		||||
        allTransitiveDependencies: null,
 | 
			
		||||
        debugGetCreateSourceHash: null,
 | 
			
		||||
        uname: uname,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  AutoDisposeFutureProviderElement<DeveloperStats?> createElement() {
 | 
			
		||||
    return _DeveloperStatsProviderElement(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  bool operator ==(Object other) {
 | 
			
		||||
    return other is DeveloperStatsProvider && other.uname == uname;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode {
 | 
			
		||||
    var hash = _SystemHash.combine(0, runtimeType.hashCode);
 | 
			
		||||
    hash = _SystemHash.combine(hash, uname.hashCode);
 | 
			
		||||
 | 
			
		||||
    return _SystemHash.finish(hash);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
 | 
			
		||||
// ignore: unused_element
 | 
			
		||||
mixin DeveloperStatsRef on AutoDisposeFutureProviderRef<DeveloperStats?> {
 | 
			
		||||
  /// The parameter `uname` of this provider.
 | 
			
		||||
  String? get uname;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _DeveloperStatsProviderElement
 | 
			
		||||
    extends AutoDisposeFutureProviderElement<DeveloperStats?>
 | 
			
		||||
    with DeveloperStatsRef {
 | 
			
		||||
  _DeveloperStatsProviderElement(super.provider);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String? get uname => (origin as DeveloperStatsProvider).uname;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String _$developersHash() => r'f52639d3c21aafbf235c8ae33f35448baf2989a1';
 | 
			
		||||
 | 
			
		||||
/// See also [developers].
 | 
			
		||||
@ProviderFor(developers)
 | 
			
		||||
final developersProvider =
 | 
			
		||||
    AutoDisposeFutureProvider<List<SnPublisher>>.internal(
 | 
			
		||||
      developers,
 | 
			
		||||
      name: r'developersProvider',
 | 
			
		||||
      debugGetCreateSourceHash:
 | 
			
		||||
          const bool.fromEnvironment('dart.vm.product')
 | 
			
		||||
              ? null
 | 
			
		||||
              : _$developersHash,
 | 
			
		||||
      dependencies: null,
 | 
			
		||||
      allTransitiveDependencies: null,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
 | 
			
		||||
// ignore: unused_element
 | 
			
		||||
typedef DevelopersRef = AutoDisposeFutureProviderRef<List<SnPublisher>>;
 | 
			
		||||
// 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
 | 
			
		||||
							
								
								
									
										12
									
								
								lib/screens/developers/new_app.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								lib/screens/developers/new_app.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:island/screens/developers/edit_app.dart';
 | 
			
		||||
 | 
			
		||||
class NewCustomAppScreen extends StatelessWidget {
 | 
			
		||||
  final String publisherName;
 | 
			
		||||
  const NewCustomAppScreen({super.key, required this.publisherName});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return EditAppScreen(publisherName: publisherName);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -6,6 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
 | 
			
		||||
import 'package:gap/gap.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/activity.dart';
 | 
			
		||||
import 'package:island/models/publisher.dart';
 | 
			
		||||
import 'package:island/models/realm.dart';
 | 
			
		||||
import 'package:island/pods/userinfo.dart';
 | 
			
		||||
import 'package:island/services/responsive.dart';
 | 
			
		||||
 
 | 
			
		||||
@@ -4,19 +4,18 @@ import 'dart:math' as math;
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:go_router/go_router.dart';
 | 
			
		||||
import 'package:flutter/services.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/user.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
import 'package:island/pods/websocket.dart';
 | 
			
		||||
import 'package:island/widgets/alert.dart';
 | 
			
		||||
import 'package:island/route.dart';
 | 
			
		||||
import 'package:island/widgets/app_scaffold.dart';
 | 
			
		||||
import 'package:island/widgets/content/markdown.dart';
 | 
			
		||||
import 'package:relative_time/relative_time.dart';
 | 
			
		||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
			
		||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
 | 
			
		||||
import 'package:styled_widget/styled_widget.dart';
 | 
			
		||||
import 'package:url_launcher/url_launcher.dart';
 | 
			
		||||
import 'package:url_launcher/url_launcher_string.dart';
 | 
			
		||||
 | 
			
		||||
part 'notification.g.dart';
 | 
			
		||||
 | 
			
		||||
@@ -180,36 +179,17 @@ class NotificationScreen extends HookConsumerWidget {
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                  onTap: () {
 | 
			
		||||
                    if (notification.meta['link'] is String) {
 | 
			
		||||
                      final href = notification.meta['link'];
 | 
			
		||||
                      final uri = Uri.tryParse(href);
 | 
			
		||||
                      if (uri == null) {
 | 
			
		||||
                        showSnackBar(
 | 
			
		||||
                          'brokenLink'.tr(args: []),
 | 
			
		||||
                          action: SnackBarAction(
 | 
			
		||||
                            label: 'copyToClipboard'.tr(),
 | 
			
		||||
                            onPressed: () {
 | 
			
		||||
                              Clipboard.setData(ClipboardData(text: href));
 | 
			
		||||
                              clearSnackBar(context);
 | 
			
		||||
                            },
 | 
			
		||||
                          ),
 | 
			
		||||
                    if (notification.meta['action_uri'] != null) {
 | 
			
		||||
                      var uri = notification.meta['action_uri'] as String;
 | 
			
		||||
                      if (uri.startsWith('/')) {
 | 
			
		||||
                        // In-app routes
 | 
			
		||||
                        rootNavigatorKey.currentContext?.push(
 | 
			
		||||
                          notification.meta['action_uri'],
 | 
			
		||||
                        );
 | 
			
		||||
                        return;
 | 
			
		||||
                      } else {
 | 
			
		||||
                        // External URLs
 | 
			
		||||
                        launchUrlString(uri);
 | 
			
		||||
                      }
 | 
			
		||||
                      if (uri.scheme == 'solian') {
 | 
			
		||||
                        context.push(
 | 
			
		||||
                          ['', uri.host, ...uri.pathSegments].join('/'),
 | 
			
		||||
                        );
 | 
			
		||||
                        return;
 | 
			
		||||
                      }
 | 
			
		||||
                      showConfirmAlert(
 | 
			
		||||
                        'openLinkConfirmDescription'.tr(args: [href]),
 | 
			
		||||
                        'openLinkConfirm'.tr(),
 | 
			
		||||
                      ).then((value) {
 | 
			
		||||
                        if (value) {
 | 
			
		||||
                          launchUrl(uri, mode: LaunchMode.externalApplication);
 | 
			
		||||
                        }
 | 
			
		||||
                      });
 | 
			
		||||
                    }
 | 
			
		||||
                  },
 | 
			
		||||
                );
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
 | 
			
		||||
import 'package:gap/gap.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/post.dart';
 | 
			
		||||
import 'package:island/models/publisher.dart';
 | 
			
		||||
import 'package:island/models/user.dart';
 | 
			
		||||
import 'package:island/pods/config.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
 
 | 
			
		||||
@@ -400,7 +400,7 @@ class _PublisherSubscriptionStatusProviderElement
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String _$publisherAppbarForcegroundColorHash() =>
 | 
			
		||||
    r'3ff2eebb48d3f3af1907052f471e648f5b14b13c';
 | 
			
		||||
    r'd781a806a242aea5c1609ec98c97c52fdd9f7db1';
 | 
			
		||||
 | 
			
		||||
/// See also [publisherAppbarForcegroundColor].
 | 
			
		||||
@ProviderFor(publisherAppbarForcegroundColor)
 | 
			
		||||
 
 | 
			
		||||
@@ -294,9 +294,9 @@ class EditRealmScreen extends HookConsumerWidget {
 | 
			
		||||
                    child:
 | 
			
		||||
                        background.value != null
 | 
			
		||||
                            ? CloudFileWidget(
 | 
			
		||||
                                item: background.value!,
 | 
			
		||||
                                fit: BoxFit.cover,
 | 
			
		||||
                              )
 | 
			
		||||
                              item: background.value!,
 | 
			
		||||
                              fit: BoxFit.cover,
 | 
			
		||||
                            )
 | 
			
		||||
                            : const SizedBox.shrink(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  onTap: () {
 | 
			
		||||
@@ -351,17 +351,36 @@ class EditRealmScreen extends HookConsumerWidget {
 | 
			
		||||
                      (_) => FocusManager.instance.primaryFocus?.unfocus(),
 | 
			
		||||
                ),
 | 
			
		||||
                const SizedBox(height: 16),
 | 
			
		||||
                CheckboxListTile(
 | 
			
		||||
                  title: const Text('isPublic').tr(),
 | 
			
		||||
                  subtitle: const Text('isPublicHint').tr(),
 | 
			
		||||
                  value: isPublic.value,
 | 
			
		||||
                  onChanged: (value) => isPublic.value = value ?? false,
 | 
			
		||||
                ),
 | 
			
		||||
                CheckboxListTile(
 | 
			
		||||
                  title: const Text('isCommunity').tr(),
 | 
			
		||||
                  subtitle: const Text('isCommunityHint').tr(),
 | 
			
		||||
                  value: isCommunity.value,
 | 
			
		||||
                  onChanged: (value) => isCommunity.value = value ?? false,
 | 
			
		||||
                Card(
 | 
			
		||||
                  margin: EdgeInsets.zero,
 | 
			
		||||
                  child: Column(
 | 
			
		||||
                    children: [
 | 
			
		||||
                      CheckboxListTile(
 | 
			
		||||
                        secondary: const Icon(Symbols.public),
 | 
			
		||||
                        title: Text('publicRealm').tr(),
 | 
			
		||||
                        subtitle: Text('publicRealmDescription').tr(),
 | 
			
		||||
                        value: isPublic.value,
 | 
			
		||||
                        onChanged: (value) {
 | 
			
		||||
                          isPublic.value = value ?? true;
 | 
			
		||||
                        },
 | 
			
		||||
                        shape: RoundedRectangleBorder(
 | 
			
		||||
                          borderRadius: BorderRadius.circular(8),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                      CheckboxListTile(
 | 
			
		||||
                        secondary: const Icon(Symbols.travel_explore),
 | 
			
		||||
                        title: Text('communityRealm').tr(),
 | 
			
		||||
                        subtitle: Text('communityRealmDescription').tr(),
 | 
			
		||||
                        value: isCommunity.value,
 | 
			
		||||
                        onChanged: (value) {
 | 
			
		||||
                          isCommunity.value = value ?? false;
 | 
			
		||||
                        },
 | 
			
		||||
                        shape: RoundedRectangleBorder(
 | 
			
		||||
                          borderRadius: BorderRadius.circular(8),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                const SizedBox(height: 16),
 | 
			
		||||
                Align(
 | 
			
		||||
@@ -435,47 +454,47 @@ class _RealmInviteSheet extends HookConsumerWidget {
 | 
			
		||||
            (items) =>
 | 
			
		||||
                items.isEmpty
 | 
			
		||||
                    ? Center(
 | 
			
		||||
                        child:
 | 
			
		||||
                            Text(
 | 
			
		||||
                              'invitesEmpty',
 | 
			
		||||
                              textAlign: TextAlign.center,
 | 
			
		||||
                            ).tr(),
 | 
			
		||||
                      )
 | 
			
		||||
                      child:
 | 
			
		||||
                          Text(
 | 
			
		||||
                            'invitesEmpty',
 | 
			
		||||
                            textAlign: TextAlign.center,
 | 
			
		||||
                          ).tr(),
 | 
			
		||||
                    )
 | 
			
		||||
                    : ListView.builder(
 | 
			
		||||
                        shrinkWrap: true,
 | 
			
		||||
                        itemCount: items.length,
 | 
			
		||||
                        itemBuilder: (context, index) {
 | 
			
		||||
                          final invite = items[index];
 | 
			
		||||
                          return ListTile(
 | 
			
		||||
                            leading: ProfilePictureWidget(
 | 
			
		||||
                              fileId: invite.realm!.picture?.id,
 | 
			
		||||
                              fallbackIcon: Symbols.group,
 | 
			
		||||
                            ),
 | 
			
		||||
                            title: Text(invite.realm!.name),
 | 
			
		||||
                            subtitle:
 | 
			
		||||
                                Text(
 | 
			
		||||
                                  invite.role >= 100
 | 
			
		||||
                                      ? 'permissionOwner'
 | 
			
		||||
                                      : invite.role >= 50
 | 
			
		||||
                                      ? 'permissionModerator'
 | 
			
		||||
                                      : 'permissionMember',
 | 
			
		||||
                                ).tr(),
 | 
			
		||||
                            trailing: Row(
 | 
			
		||||
                              mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                              children: [
 | 
			
		||||
                                IconButton(
 | 
			
		||||
                                  icon: const Icon(Symbols.check),
 | 
			
		||||
                                  onPressed: () => acceptInvite(invite),
 | 
			
		||||
                                ),
 | 
			
		||||
                                IconButton(
 | 
			
		||||
                                  icon: const Icon(Symbols.close),
 | 
			
		||||
                                  onPressed: () => declineInvite(invite),
 | 
			
		||||
                                ),
 | 
			
		||||
                              ],
 | 
			
		||||
                            ),
 | 
			
		||||
                          );
 | 
			
		||||
                        },
 | 
			
		||||
                      ),
 | 
			
		||||
                      shrinkWrap: true,
 | 
			
		||||
                      itemCount: items.length,
 | 
			
		||||
                      itemBuilder: (context, index) {
 | 
			
		||||
                        final invite = items[index];
 | 
			
		||||
                        return ListTile(
 | 
			
		||||
                          leading: ProfilePictureWidget(
 | 
			
		||||
                            fileId: invite.realm!.picture?.id,
 | 
			
		||||
                            fallbackIcon: Symbols.group,
 | 
			
		||||
                          ),
 | 
			
		||||
                          title: Text(invite.realm!.name),
 | 
			
		||||
                          subtitle:
 | 
			
		||||
                              Text(
 | 
			
		||||
                                invite.role >= 100
 | 
			
		||||
                                    ? 'permissionOwner'
 | 
			
		||||
                                    : invite.role >= 50
 | 
			
		||||
                                    ? 'permissionModerator'
 | 
			
		||||
                                    : 'permissionMember',
 | 
			
		||||
                              ).tr(),
 | 
			
		||||
                          trailing: Row(
 | 
			
		||||
                            mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                            children: [
 | 
			
		||||
                              IconButton(
 | 
			
		||||
                                icon: const Icon(Symbols.check),
 | 
			
		||||
                                onPressed: () => acceptInvite(invite),
 | 
			
		||||
                              ),
 | 
			
		||||
                              IconButton(
 | 
			
		||||
                                icon: const Icon(Symbols.close),
 | 
			
		||||
                                onPressed: () => declineInvite(invite),
 | 
			
		||||
                              ),
 | 
			
		||||
                            ],
 | 
			
		||||
                          ),
 | 
			
		||||
                        );
 | 
			
		||||
                      },
 | 
			
		||||
                    ),
 | 
			
		||||
        loading: () => const Center(child: CircularProgressIndicator()),
 | 
			
		||||
        error:
 | 
			
		||||
            (error, _) => ResponseErrorWidget(
 | 
			
		||||
 
 | 
			
		||||
@@ -341,6 +341,25 @@ class SettingsScreen extends HookConsumerWidget {
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    final behaviorSettings = [
 | 
			
		||||
      ListTile(
 | 
			
		||||
        minLeadingWidth: 48,
 | 
			
		||||
        title: Text('creatorHub').tr(),
 | 
			
		||||
        contentPadding: const EdgeInsets.only(left: 24, right: 17),
 | 
			
		||||
        leading: const Icon(Symbols.rocket_launch),
 | 
			
		||||
        trailing: const Icon(Symbols.chevron_right),
 | 
			
		||||
        onTap: () => context.push('/creators'),
 | 
			
		||||
      ),
 | 
			
		||||
 | 
			
		||||
      // Developer Hub
 | 
			
		||||
      ListTile(
 | 
			
		||||
        minLeadingWidth: 48,
 | 
			
		||||
        title: Text('developerHub').tr(),
 | 
			
		||||
        contentPadding: const EdgeInsets.only(left: 24, right: 17),
 | 
			
		||||
        leading: const Icon(Symbols.hub),
 | 
			
		||||
        trailing: const Icon(Symbols.chevron_right),
 | 
			
		||||
        onTap: () => context.push('/developers'),
 | 
			
		||||
      ),
 | 
			
		||||
 | 
			
		||||
      // Auto translate settings
 | 
			
		||||
      ListTile(
 | 
			
		||||
        minLeadingWidth: 48,
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@ class AudioCallButton extends HookConsumerWidget {
 | 
			
		||||
      try {
 | 
			
		||||
        await apiClient.post('/chat/realtime/$roomId');
 | 
			
		||||
        if (context.mounted) {
 | 
			
		||||
          context.push('/chat/call/roomId');
 | 
			
		||||
          context.push('/chat/call/$roomId');
 | 
			
		||||
        }
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        showErrorAlert(e);
 | 
			
		||||
@@ -96,7 +96,7 @@ class AudioCallButton extends HookConsumerWidget {
 | 
			
		||||
        tooltip: 'Join Ongoing Call',
 | 
			
		||||
        onPressed: () {
 | 
			
		||||
          if (context.mounted) {
 | 
			
		||||
            context.push('/chat/call/roomId');
 | 
			
		||||
            context.push('/chat/$roomId/call');
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
 
 | 
			
		||||
@@ -360,7 +360,7 @@ class CallOverlayBar extends HookConsumerWidget {
 | 
			
		||||
        ).padding(all: 16),
 | 
			
		||||
      ),
 | 
			
		||||
      onTap: () {
 | 
			
		||||
        context.push('/chat/call/callNotifier.roomId!');
 | 
			
		||||
        context.push('/chat/call/${callNotifier.roomId!}');
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -125,6 +125,7 @@ class CloudFileList extends HookConsumerWidget {
 | 
			
		||||
              if (!disableZoomIn) {
 | 
			
		||||
                context.pushTransparentRoute(
 | 
			
		||||
                  CloudFileZoomIn(item: files[i], heroTag: heroTags[i]),
 | 
			
		||||
                  rootNavigator: true,
 | 
			
		||||
                );
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:image_picker/image_picker.dart';
 | 
			
		||||
import 'package:island/models/file.dart';
 | 
			
		||||
import 'package:island/models/post.dart';
 | 
			
		||||
import 'package:island/models/publisher.dart';
 | 
			
		||||
import 'package:island/pods/config.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
import 'package:island/services/file.dart';
 | 
			
		||||
 
 | 
			
		||||
@@ -362,6 +362,7 @@ class PostItem extends HookConsumerWidget {
 | 
			
		||||
                          showModalBottomSheet(
 | 
			
		||||
                            context: context,
 | 
			
		||||
                            isScrollControlled: true,
 | 
			
		||||
                            useRootNavigator: true,
 | 
			
		||||
                            builder: (context) => PostRepliesSheet(post: item),
 | 
			
		||||
                          );
 | 
			
		||||
                        }
 | 
			
		||||
@@ -535,7 +536,7 @@ Widget _buildReferencePost(BuildContext context, SnPost item) {
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    ),
 | 
			
		||||
  ).gestures(onTap: () => context.push('/posts/referencePost.id'));
 | 
			
		||||
  ).gestures(onTap: () => context.push('/posts/${referencePost.id}'));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class PostReactionList extends HookConsumerWidget {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_hooks/flutter_hooks.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/post.dart';
 | 
			
		||||
import 'package:island/models/publisher.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
import 'package:island/screens/creators/publishers.dart';
 | 
			
		||||
import 'package:island/widgets/alert.dart';
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ 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/post.dart';
 | 
			
		||||
import 'package:island/models/publisher.dart';
 | 
			
		||||
import 'package:island/widgets/content/cloud_files.dart';
 | 
			
		||||
 | 
			
		||||
class PublisherCard extends ConsumerWidget {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@ import 'package:island/models/file.dart';
 | 
			
		||||
import 'package:island/pods/link_preview.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
import 'package:island/pods/config.dart';
 | 
			
		||||
import 'package:island/pods/userinfo.dart';
 | 
			
		||||
import 'package:island/services/file.dart';
 | 
			
		||||
import 'package:mime/mime.dart';
 | 
			
		||||
 | 
			
		||||
@@ -193,7 +192,6 @@ class _ShareSheetState extends ConsumerState<ShareSheet> {
 | 
			
		||||
    setState(() => _isLoading = true);
 | 
			
		||||
    try {
 | 
			
		||||
      final apiClient = ref.read(apiClientProvider);
 | 
			
		||||
      final userInfo = ref.read(userInfoProvider.notifier);
 | 
			
		||||
      final serverUrl = ref.read(serverUrlProvider);
 | 
			
		||||
 | 
			
		||||
      String content = _messageController.text.trim();
 | 
			
		||||
@@ -218,7 +216,7 @@ class _ShareSheetState extends ConsumerState<ShareSheet> {
 | 
			
		||||
        case ShareContentType.file:
 | 
			
		||||
          // Upload files to cloud storage
 | 
			
		||||
          if (widget.content.files?.isNotEmpty == true) {
 | 
			
		||||
            final token = await userInfo.getAccessToken();
 | 
			
		||||
            final token = ref.watch(tokenProvider)?.token;
 | 
			
		||||
            if (token == null) {
 | 
			
		||||
              throw Exception('Authentication required');
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
 | 
			
		||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 | 
			
		||||
# In Windows, build-name is used as the major, minor, and patch parts
 | 
			
		||||
# of the product and file versions while build-number is used as the build suffix.
 | 
			
		||||
version: 3.0.0+108
 | 
			
		||||
version: 3.0.0+109
 | 
			
		||||
 | 
			
		||||
environment:
 | 
			
		||||
  sdk: ^3.7.2
 | 
			
		||||
@@ -123,7 +123,7 @@ dependencies:
 | 
			
		||||
  receive_sharing_intent: ^1.8.1
 | 
			
		||||
  top_snackbar_flutter: ^3.3.0
 | 
			
		||||
  textfield_tags:
 | 
			
		||||
   git:
 | 
			
		||||
    git:
 | 
			
		||||
      url: https://github.com/lionelmennig/textfield_tags.git
 | 
			
		||||
      ref: fixes/allow-controller-re-registration
 | 
			
		||||
  mime: ^2.0.0
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user