Compare commits
	
		
			21 Commits
		
	
	
		
			a20c2598fc
			...
			3.1.0+113
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a7454edec0 | |||
| cbf1952eb7 | |||
| 6d06f0a1b4 | |||
| f2d2a9efd8 | |||
| d44c8217b0 | |||
| 446c33d8b0 | |||
| 996462f1fd | |||
| 778f6bb79f | |||
| 8747f948b9 | |||
| 9546d6e4b8 | |||
| f8d1940af6 | |||
| b2b0891d24 | |||
| 274168d4bc | |||
| 2c98b348d5 | |||
| afc7887ddd | |||
| 99ff78a3d5 | |||
| 2ad85addf6 | |||
| 552b4b2572 | |||
| 594ac39e3d | |||
| 23321171f3 | |||
| ee72d79c93 | 
@@ -41,7 +41,16 @@
 | 
			
		||||
                <action android:name="android.intent.action.MAIN" />
 | 
			
		||||
                <category android:name="android.intent.category.LAUNCHER" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <!-- Deeplinking -->
 | 
			
		||||
            <intent-filter android:autoVerify="true">
 | 
			
		||||
                <action android:name="android.intent.action.VIEW" />
 | 
			
		||||
                <category android:name="android.intent.category.DEFAULT" />
 | 
			
		||||
                <category android:name="android.intent.category.BROWSABLE" />
 | 
			
		||||
                <data android:scheme="http" android:host="solian.app" />
 | 
			
		||||
                <data android:scheme="https" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
            <!-- Share Intent Filters -->
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.SEND" />
 | 
			
		||||
 
 | 
			
		||||
@@ -375,7 +375,9 @@
 | 
			
		||||
  "postContent": "Content",
 | 
			
		||||
  "postSettings": "Settings",
 | 
			
		||||
  "postPublisherUnselected": "Publisher Unspecified",
 | 
			
		||||
  "postVisibility": "Visibility",
 | 
			
		||||
  "postType": "Post Type",
 | 
			
		||||
  "articleAttachmentHint": "Attachments must be uploaded and inserted into the article body to be visible.",
 | 
			
		||||
  "postVisibility": "Post Visibility",
 | 
			
		||||
  "postVisibilityPublic": "Public",
 | 
			
		||||
  "postVisibilityFriends": "Friends Only",
 | 
			
		||||
  "postVisibilityUnlisted": "Unlisted",
 | 
			
		||||
@@ -588,6 +590,7 @@
 | 
			
		||||
  "yes": "Yes",
 | 
			
		||||
  "navigateToChat": "Navigate to Chat",
 | 
			
		||||
  "wouldYouLikeToNavigateToChat": "Would You like to navigate to the chat?",
 | 
			
		||||
  "abuseReports": "Abuse Reports",
 | 
			
		||||
  "abuseReport": "Report",
 | 
			
		||||
  "abuseReportTitle": "Report Content",
 | 
			
		||||
  "abuseReportDescription": "Help us keep the community safe by reporting inappropriate content or behavior.",
 | 
			
		||||
 
 | 
			
		||||
@@ -45,10 +45,10 @@ PODS:
 | 
			
		||||
  - Firebase/Messaging (11.15.0):
 | 
			
		||||
    - Firebase/CoreOnly
 | 
			
		||||
    - FirebaseMessaging (~> 11.15.0)
 | 
			
		||||
  - firebase_core (3.15.0):
 | 
			
		||||
  - firebase_core (3.15.1):
 | 
			
		||||
    - Firebase/CoreOnly (= 11.15.0)
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - firebase_messaging (15.2.8):
 | 
			
		||||
  - firebase_messaging (15.2.9):
 | 
			
		||||
    - Firebase/Messaging (= 11.15.0)
 | 
			
		||||
    - firebase_core
 | 
			
		||||
    - Flutter
 | 
			
		||||
@@ -130,7 +130,7 @@ PODS:
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - irondash_engine_context (0.0.1):
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - Kingfisher (8.3.3)
 | 
			
		||||
  - Kingfisher (8.4.0)
 | 
			
		||||
  - livekit_client (2.4.9):
 | 
			
		||||
    - Flutter
 | 
			
		||||
    - flutter_webrtc
 | 
			
		||||
@@ -178,18 +178,18 @@ PODS:
 | 
			
		||||
  - sqflite_darwin (0.0.4):
 | 
			
		||||
    - Flutter
 | 
			
		||||
    - FlutterMacOS
 | 
			
		||||
  - sqlite3 (3.50.1):
 | 
			
		||||
    - sqlite3/common (= 3.50.1)
 | 
			
		||||
  - sqlite3/common (3.50.1)
 | 
			
		||||
  - sqlite3/dbstatvtab (3.50.1):
 | 
			
		||||
  - sqlite3 (3.50.2):
 | 
			
		||||
    - sqlite3/common (= 3.50.2)
 | 
			
		||||
  - sqlite3/common (3.50.2)
 | 
			
		||||
  - sqlite3/dbstatvtab (3.50.2):
 | 
			
		||||
    - sqlite3/common
 | 
			
		||||
  - sqlite3/fts5 (3.50.1):
 | 
			
		||||
  - sqlite3/fts5 (3.50.2):
 | 
			
		||||
    - sqlite3/common
 | 
			
		||||
  - sqlite3/math (3.50.1):
 | 
			
		||||
  - sqlite3/math (3.50.2):
 | 
			
		||||
    - sqlite3/common
 | 
			
		||||
  - sqlite3/perf-threadsafe (3.50.1):
 | 
			
		||||
  - sqlite3/perf-threadsafe (3.50.2):
 | 
			
		||||
    - sqlite3/common
 | 
			
		||||
  - sqlite3/rtree (3.50.1):
 | 
			
		||||
  - sqlite3/rtree (3.50.2):
 | 
			
		||||
    - sqlite3/common
 | 
			
		||||
  - sqlite3_flutter_libs (0.0.1):
 | 
			
		||||
    - Flutter
 | 
			
		||||
@@ -362,8 +362,8 @@ SPEC CHECKSUMS:
 | 
			
		||||
  DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
 | 
			
		||||
  file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
 | 
			
		||||
  Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
 | 
			
		||||
  firebase_core: c727a02c560a53f1f1e56e18f16515eb5753c492
 | 
			
		||||
  firebase_messaging: 4158969b04b667f5435731ec9d6e453bb58b0c4c
 | 
			
		||||
  firebase_core: ece862f94b2bc72ee0edbeec7ab5c7cb09fe1ab5
 | 
			
		||||
  firebase_messaging: e1a5fae495603115be1d0183bc849da748734e2b
 | 
			
		||||
  FirebaseCore: efb3893e5b94f32b86e331e3bd6dadf18b66568e
 | 
			
		||||
  FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4
 | 
			
		||||
  FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843
 | 
			
		||||
@@ -382,9 +382,9 @@ SPEC CHECKSUMS:
 | 
			
		||||
  GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
 | 
			
		||||
  image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
 | 
			
		||||
  irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486
 | 
			
		||||
  Kingfisher: ff82cb91d9266ddb56cbb2f72d32c26f00d3e5be
 | 
			
		||||
  Kingfisher: b14cc47bbfa7a3c150dd12962ee9c86338545629
 | 
			
		||||
  livekit_client: 3f79d79233a5bd13d5b541732624ef959d7c538e
 | 
			
		||||
  local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
 | 
			
		||||
  local_auth_darwin: d2e8c53ef0c4f43c646462e3415432c4dab3ae19
 | 
			
		||||
  media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
 | 
			
		||||
  media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
 | 
			
		||||
  nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
 | 
			
		||||
@@ -403,7 +403,7 @@ SPEC CHECKSUMS:
 | 
			
		||||
  shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
 | 
			
		||||
  sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418
 | 
			
		||||
  sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
 | 
			
		||||
  sqlite3: 1d85290c3321153511f6e900ede7a1608718bbd5
 | 
			
		||||
  sqlite3: 3e82a2daae39ba3b41ae6ee84a130494585460fc
 | 
			
		||||
  sqlite3_flutter_libs: e7fc8c9ea2200ff3271f08f127842131746b70e2
 | 
			
		||||
  super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4
 | 
			
		||||
  SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
	archiveVersion = 1;
 | 
			
		||||
	classes = {
 | 
			
		||||
	};
 | 
			
		||||
	objectVersion = 54;
 | 
			
		||||
	objectVersion = 77;
 | 
			
		||||
	objects = {
 | 
			
		||||
 | 
			
		||||
/* Begin PBXBuildFile section */
 | 
			
		||||
@@ -525,10 +525,14 @@
 | 
			
		||||
			inputFileListPaths = (
 | 
			
		||||
				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
 | 
			
		||||
			);
 | 
			
		||||
			inputPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			name = "[CP] Copy Pods Resources";
 | 
			
		||||
			outputFileListPaths = (
 | 
			
		||||
				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
 | 
			
		||||
			);
 | 
			
		||||
			outputPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
			shellPath = /bin/sh;
 | 
			
		||||
			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
 | 
			
		||||
@@ -586,10 +590,14 @@
 | 
			
		||||
			inputFileListPaths = (
 | 
			
		||||
				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
 | 
			
		||||
			);
 | 
			
		||||
			inputPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			name = "[CP] Embed Pods Frameworks";
 | 
			
		||||
			outputFileListPaths = (
 | 
			
		||||
				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
 | 
			
		||||
			);
 | 
			
		||||
			outputPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
			shellPath = /bin/sh;
 | 
			
		||||
			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
 | 
			
		||||
@@ -772,6 +780,7 @@
 | 
			
		||||
				ENABLE_BITCODE = NO;
 | 
			
		||||
				INFOPLIST_FILE = Runner/Info.plist;
 | 
			
		||||
				INFOPLIST_KEY_CFBundleDisplayName = Solian;
 | 
			
		||||
				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
 | 
			
		||||
				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 | 
			
		||||
				LD_RUNPATH_SEARCH_PATHS = (
 | 
			
		||||
					"$(inherited)",
 | 
			
		||||
@@ -1202,6 +1211,7 @@
 | 
			
		||||
				ENABLE_BITCODE = NO;
 | 
			
		||||
				INFOPLIST_FILE = Runner/Info.plist;
 | 
			
		||||
				INFOPLIST_KEY_CFBundleDisplayName = Solian;
 | 
			
		||||
				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
 | 
			
		||||
				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 | 
			
		||||
				LD_RUNPATH_SEARCH_PATHS = (
 | 
			
		||||
					"$(inherited)",
 | 
			
		||||
@@ -1229,6 +1239,7 @@
 | 
			
		||||
				ENABLE_BITCODE = NO;
 | 
			
		||||
				INFOPLIST_FILE = Runner/Info.plist;
 | 
			
		||||
				INFOPLIST_KEY_CFBundleDisplayName = Solian;
 | 
			
		||||
				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
 | 
			
		||||
				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 | 
			
		||||
				LD_RUNPATH_SEARCH_PATHS = (
 | 
			
		||||
					"$(inherited)",
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ import UIKit
 | 
			
		||||
        UNUserNotificationCenter.current().setNotificationCategories([replyableMessageCategory])
 | 
			
		||||
        
 | 
			
		||||
        GeneratedPluginRegistrant.register(with: self)
 | 
			
		||||
        
 | 
			
		||||
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,10 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
 | 
			
		||||
    <device id="retina6_12" orientation="portrait" appearance="light"/>
 | 
			
		||||
    <dependencies>
 | 
			
		||||
        <deployment identifier="iOS"/>
 | 
			
		||||
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
 | 
			
		||||
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
 | 
			
		||||
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
    <scenes>
 | 
			
		||||
        <!--Flutter View Controller-->
 | 
			
		||||
@@ -14,13 +16,14 @@
 | 
			
		||||
                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
 | 
			
		||||
                    </layoutGuides>
 | 
			
		||||
                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
 | 
			
		||||
                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
 | 
			
		||||
                        <rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
 | 
			
		||||
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
 | 
			
		||||
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
 | 
			
		||||
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
 | 
			
		||||
                    </view>
 | 
			
		||||
                </viewController>
 | 
			
		||||
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
 | 
			
		||||
            </objects>
 | 
			
		||||
            <point key="canvasLocation" x="-26" y="-76"/>
 | 
			
		||||
        </scene>
 | 
			
		||||
    </scenes>
 | 
			
		||||
</document>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,16 +2,10 @@
 | 
			
		||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 | 
			
		||||
<plist version="1.0">
 | 
			
		||||
<dict>
 | 
			
		||||
	<key>CLIENT_ID</key>
 | 
			
		||||
	<string>961776991058-stt7et4qvn3cpscl4r61gl1hnlatqkig.apps.googleusercontent.com</string>
 | 
			
		||||
	<key>REVERSED_CLIENT_ID</key>
 | 
			
		||||
	<string>com.googleusercontent.apps.961776991058-stt7et4qvn3cpscl4r61gl1hnlatqkig</string>
 | 
			
		||||
	<key>PLIST_VERSION</key>
 | 
			
		||||
	<string>1</string>
 | 
			
		||||
	<key>AppGroupId</key>
 | 
			
		||||
	<string>$(CUSTOM_GROUP_ID)</string>
 | 
			
		||||
	<key>BUNDLE_ID</key>
 | 
			
		||||
	<string>dev.solsynth.solian</string>
 | 
			
		||||
	<key>ITSAppUsesNonExemptEncryption</key>
 | 
			
		||||
	<false/>
 | 
			
		||||
	<key>CADisableMinimumFrameDurationOnPhone</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
	<key>CFBundleDevelopmentRegion</key>
 | 
			
		||||
@@ -32,31 +26,46 @@
 | 
			
		||||
	<string>$(FLUTTER_BUILD_NAME)</string>
 | 
			
		||||
	<key>CFBundleSignature</key>
 | 
			
		||||
	<string>????</string>
 | 
			
		||||
	<key>CFBundleURLTypes</key>
 | 
			
		||||
	<array>
 | 
			
		||||
		<dict>
 | 
			
		||||
			<key>CFBundleTypeRole</key>
 | 
			
		||||
			<string>Editor</string>
 | 
			
		||||
			<key>CFBundleURLSchemes</key>
 | 
			
		||||
			<array>
 | 
			
		||||
				<string>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
 | 
			
		||||
			</array>
 | 
			
		||||
		</dict>
 | 
			
		||||
	</array>
 | 
			
		||||
	<key>CFBundleVersion</key>
 | 
			
		||||
	<string>$(FLUTTER_BUILD_NUMBER)</string>
 | 
			
		||||
    <key>CFBundleURLTypes</key>
 | 
			
		||||
    <array>
 | 
			
		||||
        <dict>
 | 
			
		||||
            <key>CFBundleTypeRole</key>
 | 
			
		||||
            <string>Editor</string>
 | 
			
		||||
            <key>CFBundleURLSchemes</key>
 | 
			
		||||
            <array>
 | 
			
		||||
                <string>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
 | 
			
		||||
            </array>
 | 
			
		||||
        </dict>
 | 
			
		||||
    </array>
 | 
			
		||||
	<key>CLIENT_ID</key>
 | 
			
		||||
	<string>961776991058-stt7et4qvn3cpscl4r61gl1hnlatqkig.apps.googleusercontent.com</string>
 | 
			
		||||
	<key>ITSAppUsesNonExemptEncryption</key>
 | 
			
		||||
	<false/>
 | 
			
		||||
	<key>LSRequiresIPhoneOS</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
	<key>NSCalendarsUsageDescription</key>
 | 
			
		||||
	<string>Grant access to Calander help us to shows Solar Calander with your own events.</string>
 | 
			
		||||
	<key>NSCameraUsageDescription</key>
 | 
			
		||||
	<string>Grant access to Camera will allow Solian take photo or video for your post.</string>
 | 
			
		||||
	<key>NSFaceIDUsageDescription</key>
 | 
			
		||||
	<string>Allow the Solar Network verify your ownership of the logged in account and continue your action quickly.</string>
 | 
			
		||||
	<key>NSMicrophoneUsageDescription</key>
 | 
			
		||||
	<string>Grant access to Microphone will allow Solian record audio for your post.</string>
 | 
			
		||||
	<key>NSPhotoLibraryAddUsageDescription</key>
 | 
			
		||||
	<string>Grant access to Photo Library will allow Solian download photo to album for you.</string>
 | 
			
		||||
	<key>NSPhotoLibraryUsageDescription</key>
 | 
			
		||||
	<string>Grant access to Photo Library will allow Solian upload photo or video for your post.</string>
 | 
			
		||||
	<key>NSUserActivityTypes</key>
 | 
			
		||||
	<array>
 | 
			
		||||
		<string>INStartCallIntent</string>
 | 
			
		||||
		<string>INSendMessageIntent</string>
 | 
			
		||||
	</array>
 | 
			
		||||
	<key>PLIST_VERSION</key>
 | 
			
		||||
	<string>1</string>
 | 
			
		||||
	<key>REVERSED_CLIENT_ID</key>
 | 
			
		||||
	<string>com.googleusercontent.apps.961776991058-stt7et4qvn3cpscl4r61gl1hnlatqkig</string>
 | 
			
		||||
	<key>UIApplicationSupportsIndirectInputEvents</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
	<key>UIBackgroundModes</key>
 | 
			
		||||
@@ -74,25 +83,16 @@
 | 
			
		||||
	<false/>
 | 
			
		||||
	<key>UISupportedInterfaceOrientations</key>
 | 
			
		||||
	<array>
 | 
			
		||||
		<string>UIInterfaceOrientationPortrait</string>
 | 
			
		||||
		<string>UIInterfaceOrientationLandscapeLeft</string>
 | 
			
		||||
		<string>UIInterfaceOrientationLandscapeRight</string>
 | 
			
		||||
		<string>UIInterfaceOrientationPortrait</string>
 | 
			
		||||
	</array>
 | 
			
		||||
	<key>UISupportedInterfaceOrientations~ipad</key>
 | 
			
		||||
	<array>
 | 
			
		||||
		<string>UIInterfaceOrientationPortrait</string>
 | 
			
		||||
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
 | 
			
		||||
		<string>UIInterfaceOrientationLandscapeLeft</string>
 | 
			
		||||
		<string>UIInterfaceOrientationLandscapeRight</string>
 | 
			
		||||
	</array>
 | 
			
		||||
	<key>NSFaceIDUsageDescription</key>
 | 
			
		||||
	<string>Allow the Solar Network verify your ownership of the logged in account and continue your action quickly.</string>
 | 
			
		||||
	<key>AppGroupId</key>
 | 
			
		||||
	<string>$(CUSTOM_GROUP_ID)</string>
 | 
			
		||||
	<key>NSUserActivityTypes</key>
 | 
			
		||||
	<array>
 | 
			
		||||
		<string>INStartCallIntent</string>
 | 
			
		||||
		<string>INSendMessageIntent</string>
 | 
			
		||||
		<string>UIInterfaceOrientationPortrait</string>
 | 
			
		||||
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
 | 
			
		||||
	</array>
 | 
			
		||||
</dict>
 | 
			
		||||
</plist>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								lib/models/abuse_report.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								lib/models/abuse_report.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
import 'package:freezed_annotation/freezed_annotation.dart';
 | 
			
		||||
 | 
			
		||||
part 'abuse_report.freezed.dart';
 | 
			
		||||
part 'abuse_report.g.dart';
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
sealed class SnAbuseReport with _$SnAbuseReport {
 | 
			
		||||
  const factory SnAbuseReport({
 | 
			
		||||
    required String id,
 | 
			
		||||
    required String resourceIdentifier,
 | 
			
		||||
    required int type,
 | 
			
		||||
    required String reason,
 | 
			
		||||
    required DateTime? resolvedAt,
 | 
			
		||||
    required String? resolution,
 | 
			
		||||
    required String accountId,
 | 
			
		||||
    required DateTime createdAt,
 | 
			
		||||
    required DateTime updatedAt,
 | 
			
		||||
    required DateTime? deletedAt,
 | 
			
		||||
  }) = _SnAbuseReport;
 | 
			
		||||
 | 
			
		||||
  factory SnAbuseReport.fromJson(Map<String, dynamic> json) =>
 | 
			
		||||
      _$SnAbuseReportFromJson(json);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										175
									
								
								lib/models/abuse_report.freezed.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								lib/models/abuse_report.freezed.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,175 @@
 | 
			
		||||
// 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 'abuse_report.dart';
 | 
			
		||||
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
// FreezedGenerator
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
 | 
			
		||||
// dart format off
 | 
			
		||||
T _$identity<T>(T value) => value;
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
mixin _$SnAbuseReport {
 | 
			
		||||
 | 
			
		||||
 String get id; String get resourceIdentifier; int get type; String get reason; DateTime? get resolvedAt; String? get resolution; String get accountId; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
 | 
			
		||||
/// Create a copy of SnAbuseReport
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
$SnAbuseReportCopyWith<SnAbuseReport> get copyWith => _$SnAbuseReportCopyWithImpl<SnAbuseReport>(this as SnAbuseReport, _$identity);
 | 
			
		||||
 | 
			
		||||
  /// Serializes this SnAbuseReport to a JSON map.
 | 
			
		||||
  Map<String, dynamic> toJson();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAbuseReport&&(identical(other.id, id) || other.id == id)&&(identical(other.resourceIdentifier, resourceIdentifier) || other.resourceIdentifier == resourceIdentifier)&&(identical(other.type, type) || other.type == type)&&(identical(other.reason, reason) || other.reason == reason)&&(identical(other.resolvedAt, resolvedAt) || other.resolvedAt == resolvedAt)&&(identical(other.resolution, resolution) || other.resolution == resolution)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,id,resourceIdentifier,type,reason,resolvedAt,resolution,accountId,createdAt,updatedAt,deletedAt);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'SnAbuseReport(id: $id, resourceIdentifier: $resourceIdentifier, type: $type, reason: $reason, resolvedAt: $resolvedAt, resolution: $resolution, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class $SnAbuseReportCopyWith<$Res>  {
 | 
			
		||||
  factory $SnAbuseReportCopyWith(SnAbuseReport value, $Res Function(SnAbuseReport) _then) = _$SnAbuseReportCopyWithImpl;
 | 
			
		||||
@useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String id, String resourceIdentifier, int type, String reason, DateTime? resolvedAt, String? resolution, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class _$SnAbuseReportCopyWithImpl<$Res>
 | 
			
		||||
    implements $SnAbuseReportCopyWith<$Res> {
 | 
			
		||||
  _$SnAbuseReportCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final SnAbuseReport _self;
 | 
			
		||||
  final $Res Function(SnAbuseReport) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of SnAbuseReport
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? resourceIdentifier = null,Object? type = null,Object? reason = null,Object? resolvedAt = freezed,Object? resolution = freezed,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
 | 
			
		||||
  return _then(_self.copyWith(
 | 
			
		||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,resourceIdentifier: null == resourceIdentifier ? _self.resourceIdentifier : resourceIdentifier // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as int,reason: null == reason ? _self.reason : reason // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,resolvedAt: freezed == resolvedAt ? _self.resolvedAt : resolvedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,resolution: freezed == resolution ? _self.resolution : resolution // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
@JsonSerializable()
 | 
			
		||||
 | 
			
		||||
class _SnAbuseReport implements SnAbuseReport {
 | 
			
		||||
  const _SnAbuseReport({required this.id, required this.resourceIdentifier, required this.type, required this.reason, required this.resolvedAt, required this.resolution, required this.accountId, required this.createdAt, required this.updatedAt, required this.deletedAt});
 | 
			
		||||
  factory _SnAbuseReport.fromJson(Map<String, dynamic> json) => _$SnAbuseReportFromJson(json);
 | 
			
		||||
 | 
			
		||||
@override final  String id;
 | 
			
		||||
@override final  String resourceIdentifier;
 | 
			
		||||
@override final  int type;
 | 
			
		||||
@override final  String reason;
 | 
			
		||||
@override final  DateTime? resolvedAt;
 | 
			
		||||
@override final  String? resolution;
 | 
			
		||||
@override final  String accountId;
 | 
			
		||||
@override final  DateTime createdAt;
 | 
			
		||||
@override final  DateTime updatedAt;
 | 
			
		||||
@override final  DateTime? deletedAt;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of SnAbuseReport
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@pragma('vm:prefer-inline')
 | 
			
		||||
_$SnAbuseReportCopyWith<_SnAbuseReport> get copyWith => __$SnAbuseReportCopyWithImpl<_SnAbuseReport>(this, _$identity);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
Map<String, dynamic> toJson() {
 | 
			
		||||
  return _$SnAbuseReportToJson(this, );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
bool operator ==(Object other) {
 | 
			
		||||
  return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAbuseReport&&(identical(other.id, id) || other.id == id)&&(identical(other.resourceIdentifier, resourceIdentifier) || other.resourceIdentifier == resourceIdentifier)&&(identical(other.type, type) || other.type == type)&&(identical(other.reason, reason) || other.reason == reason)&&(identical(other.resolvedAt, resolvedAt) || other.resolvedAt == resolvedAt)&&(identical(other.resolution, resolution) || other.resolution == resolution)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonKey(includeFromJson: false, includeToJson: false)
 | 
			
		||||
@override
 | 
			
		||||
int get hashCode => Object.hash(runtimeType,id,resourceIdentifier,type,reason,resolvedAt,resolution,accountId,createdAt,updatedAt,deletedAt);
 | 
			
		||||
 | 
			
		||||
@override
 | 
			
		||||
String toString() {
 | 
			
		||||
  return 'SnAbuseReport(id: $id, resourceIdentifier: $resourceIdentifier, type: $type, reason: $reason, resolvedAt: $resolvedAt, resolution: $resolution, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// @nodoc
 | 
			
		||||
abstract mixin class _$SnAbuseReportCopyWith<$Res> implements $SnAbuseReportCopyWith<$Res> {
 | 
			
		||||
  factory _$SnAbuseReportCopyWith(_SnAbuseReport value, $Res Function(_SnAbuseReport) _then) = __$SnAbuseReportCopyWithImpl;
 | 
			
		||||
@override @useResult
 | 
			
		||||
$Res call({
 | 
			
		||||
 String id, String resourceIdentifier, int type, String reason, DateTime? resolvedAt, String? resolution, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/// @nodoc
 | 
			
		||||
class __$SnAbuseReportCopyWithImpl<$Res>
 | 
			
		||||
    implements _$SnAbuseReportCopyWith<$Res> {
 | 
			
		||||
  __$SnAbuseReportCopyWithImpl(this._self, this._then);
 | 
			
		||||
 | 
			
		||||
  final _SnAbuseReport _self;
 | 
			
		||||
  final $Res Function(_SnAbuseReport) _then;
 | 
			
		||||
 | 
			
		||||
/// Create a copy of SnAbuseReport
 | 
			
		||||
/// with the given fields replaced by the non-null parameter values.
 | 
			
		||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? resourceIdentifier = null,Object? type = null,Object? reason = null,Object? resolvedAt = freezed,Object? resolution = freezed,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
 | 
			
		||||
  return _then(_SnAbuseReport(
 | 
			
		||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,resourceIdentifier: null == resourceIdentifier ? _self.resourceIdentifier : resourceIdentifier // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as int,reason: null == reason ? _self.reason : reason // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,resolvedAt: freezed == resolvedAt ? _self.resolvedAt : resolvedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,resolution: freezed == resolution ? _self.resolution : resolution // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
 | 
			
		||||
as DateTime?,
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dart format on
 | 
			
		||||
							
								
								
									
										41
									
								
								lib/models/abuse_report.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								lib/models/abuse_report.g.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
			
		||||
 | 
			
		||||
part of 'abuse_report.dart';
 | 
			
		||||
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
// JsonSerializableGenerator
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
 | 
			
		||||
_SnAbuseReport _$SnAbuseReportFromJson(Map<String, dynamic> json) =>
 | 
			
		||||
    _SnAbuseReport(
 | 
			
		||||
      id: json['id'] as String,
 | 
			
		||||
      resourceIdentifier: json['resource_identifier'] as String,
 | 
			
		||||
      type: (json['type'] as num).toInt(),
 | 
			
		||||
      reason: json['reason'] as String,
 | 
			
		||||
      resolvedAt:
 | 
			
		||||
          json['resolved_at'] == null
 | 
			
		||||
              ? null
 | 
			
		||||
              : DateTime.parse(json['resolved_at'] as String),
 | 
			
		||||
      resolution: json['resolution'] as String?,
 | 
			
		||||
      accountId: json['account_id'] as String,
 | 
			
		||||
      createdAt: DateTime.parse(json['created_at'] as String),
 | 
			
		||||
      updatedAt: DateTime.parse(json['updated_at'] as String),
 | 
			
		||||
      deletedAt:
 | 
			
		||||
          json['deleted_at'] == null
 | 
			
		||||
              ? null
 | 
			
		||||
              : DateTime.parse(json['deleted_at'] as String),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
Map<String, dynamic> _$SnAbuseReportToJson(_SnAbuseReport instance) =>
 | 
			
		||||
    <String, dynamic>{
 | 
			
		||||
      'id': instance.id,
 | 
			
		||||
      'resource_identifier': instance.resourceIdentifier,
 | 
			
		||||
      'type': instance.type,
 | 
			
		||||
      'reason': instance.reason,
 | 
			
		||||
      'resolved_at': instance.resolvedAt?.toIso8601String(),
 | 
			
		||||
      'resolution': instance.resolution,
 | 
			
		||||
      'account_id': instance.accountId,
 | 
			
		||||
      'created_at': instance.createdAt.toIso8601String(),
 | 
			
		||||
      'updated_at': instance.updatedAt.toIso8601String(),
 | 
			
		||||
      'deleted_at': instance.deletedAt?.toIso8601String(),
 | 
			
		||||
    };
 | 
			
		||||
							
								
								
									
										38
									
								
								lib/models/abuse_report_type.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								lib/models/abuse_report_type.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
enum AbuseReportType {
 | 
			
		||||
  copyright(0),
 | 
			
		||||
  harassment(1),
 | 
			
		||||
  impersonation(2),
 | 
			
		||||
  offensiveContent(3),
 | 
			
		||||
  spam(4),
 | 
			
		||||
  privacyViolation(5),
 | 
			
		||||
  illegalContent(6),
 | 
			
		||||
  other(7);
 | 
			
		||||
 | 
			
		||||
  const AbuseReportType(this.value);
 | 
			
		||||
  final int value;
 | 
			
		||||
 | 
			
		||||
  static AbuseReportType fromValue(int value) {
 | 
			
		||||
    return values.firstWhere((e) => e.value == value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String get displayName {
 | 
			
		||||
    switch (this) {
 | 
			
		||||
      case AbuseReportType.copyright:
 | 
			
		||||
        return 'Copyright';
 | 
			
		||||
      case AbuseReportType.harassment:
 | 
			
		||||
        return 'Harassment';
 | 
			
		||||
      case AbuseReportType.impersonation:
 | 
			
		||||
        return 'Impersonation';
 | 
			
		||||
      case AbuseReportType.offensiveContent:
 | 
			
		||||
        return 'Offensive Content';
 | 
			
		||||
      case AbuseReportType.spam:
 | 
			
		||||
        return 'Spam';
 | 
			
		||||
      case AbuseReportType.privacyViolation:
 | 
			
		||||
        return 'Privacy Violation';
 | 
			
		||||
      case AbuseReportType.illegalContent:
 | 
			
		||||
        return 'Illegal Content';
 | 
			
		||||
      case AbuseReportType.other:
 | 
			
		||||
        return 'Other';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -65,12 +65,13 @@ final apiClientProvider = Provider<Dio>((ref) {
 | 
			
		||||
  final serverUrl = ref.watch(serverUrlProvider);
 | 
			
		||||
  final dio = Dio(
 | 
			
		||||
    BaseOptions(
 | 
			
		||||
      baseUrl: serverUrl,
 | 
			
		||||
      baseUrl: '$serverUrl/api',
 | 
			
		||||
      connectTimeout: const Duration(seconds: 10),
 | 
			
		||||
      receiveTimeout: const Duration(seconds: 10),
 | 
			
		||||
      headers: {
 | 
			
		||||
        'Accept': 'application/json',
 | 
			
		||||
        'Content-Type': 'application/json',
 | 
			
		||||
        'X-Client': 'Solian',
 | 
			
		||||
      },
 | 
			
		||||
    ),
 | 
			
		||||
  );
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ import 'package:island/screens/posts/post_search.dart';
 | 
			
		||||
import 'package:island/widgets/app_wrapper.dart';
 | 
			
		||||
import 'package:island/screens/tabs.dart';
 | 
			
		||||
import 'package:island/screens/explore.dart';
 | 
			
		||||
import 'package:island/screens/article_detail_screen.dart';
 | 
			
		||||
import 'package:island/screens/discovery/article_detail.dart';
 | 
			
		||||
import 'package:island/screens/account.dart';
 | 
			
		||||
import 'package:island/screens/notification.dart';
 | 
			
		||||
import 'package:island/screens/wallet.dart';
 | 
			
		||||
@@ -41,6 +41,8 @@ import 'package:island/screens/realm/realms.dart';
 | 
			
		||||
import 'package:island/screens/realm/realm_detail.dart';
 | 
			
		||||
import 'package:island/screens/account/event_calendar.dart';
 | 
			
		||||
import 'package:island/screens/discovery/realms.dart';
 | 
			
		||||
import 'package:island/screens/reports/report_detail.dart';
 | 
			
		||||
import 'package:island/screens/reports/report_list.dart';
 | 
			
		||||
 | 
			
		||||
// Shell route keys for nested navigation
 | 
			
		||||
final rootNavigatorKey = GlobalKey<NavigatorState>();
 | 
			
		||||
@@ -258,6 +260,19 @@ final routerProvider = Provider<GoRouter>((ref) {
 | 
			
		||||
            builder: (context, state) => const AboutScreen(),
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
          GoRoute(
 | 
			
		||||
            path: '/safety/reports/me',
 | 
			
		||||
            builder: (context, state) => const AbuseReportListScreen(),
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
          GoRoute(
 | 
			
		||||
            path: '/safety/reports/me/:id',
 | 
			
		||||
            builder: (context, state) {
 | 
			
		||||
              final id = state.pathParameters['id']!;
 | 
			
		||||
              return AbuseReportDetailScreen(reportId: id);
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
          // Main tabs with TabsScreen shell
 | 
			
		||||
          ShellRoute(
 | 
			
		||||
            navigatorKey: _tabsShellKey,
 | 
			
		||||
@@ -399,7 +414,7 @@ final routerProvider = Provider<GoRouter>((ref) {
 | 
			
		||||
                    builder: (context, state) => const LevelingScreen(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  GoRoute(
 | 
			
		||||
                    path: '/account/settings',
 | 
			
		||||
                    path: '/account/me/settings',
 | 
			
		||||
                    builder: (context, state) => const AccountSettingsScreen(),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import 'package:island/pods/network.dart';
 | 
			
		||||
import 'package:island/services/notify.dart';
 | 
			
		||||
import 'package:island/services/udid.native.dart';
 | 
			
		||||
import 'package:island/widgets/alert.dart';
 | 
			
		||||
import 'package:island/widgets/app_scaffold.dart';
 | 
			
		||||
import 'package:material_symbols_icons/symbols.dart';
 | 
			
		||||
import 'package:package_info_plus/package_info_plus.dart';
 | 
			
		||||
import 'package:styled_widget/styled_widget.dart';
 | 
			
		||||
@@ -90,7 +91,7 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final theme = Theme.of(context);
 | 
			
		||||
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      appBar: AppBar(title: Text('about'.tr()), elevation: 0),
 | 
			
		||||
      body:
 | 
			
		||||
          _isLoading
 | 
			
		||||
 
 | 
			
		||||
@@ -222,9 +222,17 @@ class AccountScreen extends HookConsumerWidget {
 | 
			
		||||
              contentPadding: EdgeInsets.symmetric(horizontal: 24),
 | 
			
		||||
              title: Text('relationships').tr(),
 | 
			
		||||
              onTap: () {
 | 
			
		||||
                context.push('/account/relationship');
 | 
			
		||||
                context.push('/account/relationships');
 | 
			
		||||
              },
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              minTileHeight: 48,
 | 
			
		||||
              title: Text('abuseReports').tr(),
 | 
			
		||||
              contentPadding: const EdgeInsets.only(left: 24, right: 17),
 | 
			
		||||
              leading: const Icon(Symbols.gavel),
 | 
			
		||||
              trailing: const Icon(Symbols.chevron_right),
 | 
			
		||||
              onTap: () => context.push('/safety/reports/me'),
 | 
			
		||||
            ),
 | 
			
		||||
            const Divider(height: 1).padding(vertical: 8),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              minTileHeight: 48,
 | 
			
		||||
@@ -328,7 +336,7 @@ class _UnauthorizedAccountScreen extends StatelessWidget {
 | 
			
		||||
                  child: Card(
 | 
			
		||||
                    child: InkWell(
 | 
			
		||||
                      onTap: () {
 | 
			
		||||
                        context.push('/auth/create');
 | 
			
		||||
                        context.push('/auth/create-account');
 | 
			
		||||
                      },
 | 
			
		||||
                      child: Padding(
 | 
			
		||||
                        padding: const EdgeInsets.all(16),
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@ import 'package:island/widgets/account/status.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/safety/abuse_report_helper.dart';
 | 
			
		||||
import 'package:material_symbols_icons/symbols.dart';
 | 
			
		||||
import 'package:palette_generator/palette_generator.dart';
 | 
			
		||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
			
		||||
@@ -143,6 +144,23 @@ class AccountProfileScreen extends HookConsumerWidget {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Future<void> blockAction() async {
 | 
			
		||||
      showLoadingModal(context);
 | 
			
		||||
      try {
 | 
			
		||||
        final client = ref.watch(apiClientProvider);
 | 
			
		||||
        if (accountRelationship.value == null) {
 | 
			
		||||
          await client.post('/relationships/${account.value!.id}/block');
 | 
			
		||||
        } else {
 | 
			
		||||
          await client.delete('/relationships/${account.value!.id}/block');
 | 
			
		||||
        }
 | 
			
		||||
        ref.invalidate(accountRelationshipProvider(name));
 | 
			
		||||
      } catch (err) {
 | 
			
		||||
        showErrorAlert(err);
 | 
			
		||||
      } finally {
 | 
			
		||||
        if (context.mounted) hideLoadingModal(context);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Future<void> directMessageAction() async {
 | 
			
		||||
      if (!account.hasValue) return;
 | 
			
		||||
      if (accountChat.value != null) {
 | 
			
		||||
@@ -396,49 +414,108 @@ class AccountProfileScreen extends HookConsumerWidget {
 | 
			
		||||
                    child: Row(
 | 
			
		||||
                      spacing: 8,
 | 
			
		||||
                      children: [
 | 
			
		||||
                        Expanded(
 | 
			
		||||
                          child: FilledButton.icon(
 | 
			
		||||
                            style: ButtonStyle(
 | 
			
		||||
                              backgroundColor: WidgetStatePropertyAll(
 | 
			
		||||
                                accountRelationship.value == null
 | 
			
		||||
                                    ? null
 | 
			
		||||
                                    : Theme.of(context).colorScheme.secondary,
 | 
			
		||||
                              ),
 | 
			
		||||
                              foregroundColor: WidgetStatePropertyAll(
 | 
			
		||||
                                accountRelationship.value == null
 | 
			
		||||
                                    ? null
 | 
			
		||||
                                    : Theme.of(context).colorScheme.onSecondary,
 | 
			
		||||
                              ),
 | 
			
		||||
                            ),
 | 
			
		||||
                            onPressed: relationshipAction,
 | 
			
		||||
                            label:
 | 
			
		||||
                                Text(
 | 
			
		||||
                        if (accountRelationship.value == null ||
 | 
			
		||||
                            accountRelationship.value!.status > -100)
 | 
			
		||||
                          Expanded(
 | 
			
		||||
                            child: FilledButton.icon(
 | 
			
		||||
                              style: ButtonStyle(
 | 
			
		||||
                                backgroundColor: WidgetStatePropertyAll(
 | 
			
		||||
                                  accountRelationship.value == null
 | 
			
		||||
                                      ? 'addFriendShort'
 | 
			
		||||
                                      : 'added',
 | 
			
		||||
                                ).tr(),
 | 
			
		||||
                            icon:
 | 
			
		||||
                                accountRelationship.value == null
 | 
			
		||||
                                    ? const Icon(Symbols.person_add)
 | 
			
		||||
                                    : const Icon(Symbols.person_check),
 | 
			
		||||
                                      ? null
 | 
			
		||||
                                      : Theme.of(context).colorScheme.secondary,
 | 
			
		||||
                                ),
 | 
			
		||||
                                foregroundColor: WidgetStatePropertyAll(
 | 
			
		||||
                                  accountRelationship.value == null
 | 
			
		||||
                                      ? null
 | 
			
		||||
                                      : Theme.of(
 | 
			
		||||
                                        context,
 | 
			
		||||
                                      ).colorScheme.onSecondary,
 | 
			
		||||
                                ),
 | 
			
		||||
                              ),
 | 
			
		||||
                              onPressed: relationshipAction,
 | 
			
		||||
                              label:
 | 
			
		||||
                                  Text(
 | 
			
		||||
                                    accountRelationship.value == null
 | 
			
		||||
                                        ? 'addFriendShort'
 | 
			
		||||
                                        : 'added',
 | 
			
		||||
                                  ).tr(),
 | 
			
		||||
                              icon:
 | 
			
		||||
                                  accountRelationship.value == null
 | 
			
		||||
                                      ? const Icon(Symbols.person_add)
 | 
			
		||||
                                      : const Icon(Symbols.person_check),
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                        ),
 | 
			
		||||
                        Expanded(
 | 
			
		||||
                          child: FilledButton.icon(
 | 
			
		||||
                            onPressed: directMessageAction,
 | 
			
		||||
                            icon: const Icon(Symbols.message),
 | 
			
		||||
                            label:
 | 
			
		||||
                                Text(
 | 
			
		||||
                                  accountChat.value == null
 | 
			
		||||
                                      ? 'createDirectMessage'
 | 
			
		||||
                                      : 'gotoDirectMessage',
 | 
			
		||||
                                  maxLines: 1,
 | 
			
		||||
                                ).tr(),
 | 
			
		||||
                        if (accountRelationship.value == null ||
 | 
			
		||||
                            accountRelationship.value!.status <= -100)
 | 
			
		||||
                          Expanded(
 | 
			
		||||
                            child: FilledButton.icon(
 | 
			
		||||
                              style: ButtonStyle(
 | 
			
		||||
                                backgroundColor: WidgetStatePropertyAll(
 | 
			
		||||
                                  accountRelationship.value == null
 | 
			
		||||
                                      ? null
 | 
			
		||||
                                      : Theme.of(context).colorScheme.secondary,
 | 
			
		||||
                                ),
 | 
			
		||||
                                foregroundColor: WidgetStatePropertyAll(
 | 
			
		||||
                                  accountRelationship.value == null
 | 
			
		||||
                                      ? null
 | 
			
		||||
                                      : Theme.of(
 | 
			
		||||
                                        context,
 | 
			
		||||
                                      ).colorScheme.onSecondary,
 | 
			
		||||
                                ),
 | 
			
		||||
                              ),
 | 
			
		||||
                              onPressed: blockAction,
 | 
			
		||||
                              label:
 | 
			
		||||
                                  Text(
 | 
			
		||||
                                    accountRelationship.value == null
 | 
			
		||||
                                        ? 'blockUser'
 | 
			
		||||
                                        : 'unblockUser',
 | 
			
		||||
                                  ).tr(),
 | 
			
		||||
                              icon:
 | 
			
		||||
                                  accountRelationship.value == null
 | 
			
		||||
                                      ? const Icon(Symbols.block)
 | 
			
		||||
                                      : const Icon(Symbols.person_cancel),
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ],
 | 
			
		||||
                    ).padding(horizontal: 16),
 | 
			
		||||
                  ),
 | 
			
		||||
                SliverToBoxAdapter(
 | 
			
		||||
                  child: Row(
 | 
			
		||||
                    spacing: 8,
 | 
			
		||||
                    children: [
 | 
			
		||||
                      Expanded(
 | 
			
		||||
                        child: FilledButton.icon(
 | 
			
		||||
                          onPressed: directMessageAction,
 | 
			
		||||
                          icon: const Icon(Symbols.message),
 | 
			
		||||
                          label:
 | 
			
		||||
                              Text(
 | 
			
		||||
                                accountChat.value == null
 | 
			
		||||
                                    ? 'createDirectMessage'
 | 
			
		||||
                                    : 'gotoDirectMessage',
 | 
			
		||||
                                maxLines: 1,
 | 
			
		||||
                              ).tr(),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                      IconButton.filled(
 | 
			
		||||
                        onPressed: () {
 | 
			
		||||
                          showAbuseReportSheet(
 | 
			
		||||
                            context,
 | 
			
		||||
                            resourceIdentifier: 'account/${data.id}',
 | 
			
		||||
                          );
 | 
			
		||||
                        },
 | 
			
		||||
                        icon: Icon(
 | 
			
		||||
                          Symbols.flag,
 | 
			
		||||
                          color: Theme.of(context).colorScheme.onError,
 | 
			
		||||
                        ),
 | 
			
		||||
                        style: ButtonStyle(
 | 
			
		||||
                          backgroundColor: WidgetStatePropertyAll(
 | 
			
		||||
                            Theme.of(context).colorScheme.error,
 | 
			
		||||
                          ),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ],
 | 
			
		||||
                  ).padding(horizontal: 16, top: 4),
 | 
			
		||||
                ),
 | 
			
		||||
                SliverToBoxAdapter(
 | 
			
		||||
                  child: const Divider(height: 1).padding(top: 12),
 | 
			
		||||
                ),
 | 
			
		||||
 
 | 
			
		||||
@@ -395,7 +395,7 @@ class _AccountAppbarForcegroundColorProviderElement
 | 
			
		||||
  String get uname => (origin as AccountAppbarForcegroundColorProvider).uname;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String _$accountDirectChatHash() => r'60d0015fc2a3c8fc2190bb41d6818cf3027d9d0a';
 | 
			
		||||
String _$accountDirectChatHash() => r'3d28c8ba8079159f724fe3cd47bbe00db55cedcc';
 | 
			
		||||
 | 
			
		||||
/// See also [accountDirectChat].
 | 
			
		||||
@ProviderFor(accountDirectChat)
 | 
			
		||||
@@ -517,7 +517,7 @@ class _AccountDirectChatProviderElement
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String _$accountRelationshipHash() =>
 | 
			
		||||
    r'cb7d0d3f8cd4f23ad9d2d529872c540dac483d4f';
 | 
			
		||||
    r'0be2420e1f6a65b8dcead9617191471924aaf232';
 | 
			
		||||
 | 
			
		||||
/// See also [accountRelationship].
 | 
			
		||||
@ProviderFor(accountRelationship)
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ class StickersScreen extends HookConsumerWidget {
 | 
			
		||||
        actions: [
 | 
			
		||||
          IconButton(
 | 
			
		||||
            onPressed: () {
 | 
			
		||||
              context.push('/creators/stickers/new?pubName=pubName').then((
 | 
			
		||||
              context.push('/creators/stickers/new?pubName=$pubName').then((
 | 
			
		||||
                value,
 | 
			
		||||
              ) {
 | 
			
		||||
                if (value != null) {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/webfeed.dart';
 | 
			
		||||
import 'package:island/pods/webfeed.dart';
 | 
			
		||||
import 'package:island/widgets/alert.dart';
 | 
			
		||||
import 'package:island/widgets/app_scaffold.dart';
 | 
			
		||||
import 'package:material_symbols_icons/symbols.dart';
 | 
			
		||||
import 'package:styled_widget/styled_widget.dart';
 | 
			
		||||
 | 
			
		||||
@@ -183,7 +184,7 @@ class WebFeedEditScreen extends HookConsumerWidget {
 | 
			
		||||
      }
 | 
			
		||||
    }, [pubName, feedId, ref, context]);
 | 
			
		||||
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        title: Text(hasFeedId ? 'Edit Web Feed' : 'New Web Feed'),
 | 
			
		||||
        actions: [
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/webfeed.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
import 'package:island/widgets/app_scaffold.dart';
 | 
			
		||||
import 'package:island/widgets/web_article_card.dart';
 | 
			
		||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
			
		||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
 | 
			
		||||
@@ -124,7 +125,7 @@ class ArticlesScreen extends ConsumerWidget {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      appBar: AppBar(title: Text(title ?? 'Articles')),
 | 
			
		||||
      body: Center(
 | 
			
		||||
        child: ConstrainedBox(
 | 
			
		||||
 
 | 
			
		||||
@@ -338,7 +338,7 @@ class _ActivityListView extends HookConsumerWidget {
 | 
			
		||||
                            bottom: 16,
 | 
			
		||||
                          )
 | 
			
		||||
                          : null,
 | 
			
		||||
                  onRefresh: (_) {
 | 
			
		||||
                  onRefresh: () {
 | 
			
		||||
                    activitiesNotifier.forceRefresh();
 | 
			
		||||
                  },
 | 
			
		||||
                  onUpdate: (post) {
 | 
			
		||||
 
 | 
			
		||||
@@ -321,8 +321,15 @@ class ArticleComposeScreen extends HookConsumerWidget {
 | 
			
		||||
            builder: (context, attachments, _) {
 | 
			
		||||
              if (attachments.isEmpty) return const SizedBox.shrink();
 | 
			
		||||
              return Column(
 | 
			
		||||
                crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                children: [
 | 
			
		||||
                  const Gap(16),
 | 
			
		||||
                  Text(
 | 
			
		||||
                    'articleAttachmentHint'.tr(),
 | 
			
		||||
                    style: Theme.of(context).textTheme.bodySmall?.copyWith(
 | 
			
		||||
                      color: Theme.of(context).colorScheme.onSurfaceVariant,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ).padding(bottom: 8),
 | 
			
		||||
                  ValueListenableBuilder<Map<int, double>>(
 | 
			
		||||
                    valueListenable: state.attachmentProgress,
 | 
			
		||||
                    builder: (context, progressMap, _) {
 | 
			
		||||
@@ -332,8 +339,8 @@ class ArticleComposeScreen extends HookConsumerWidget {
 | 
			
		||||
                        children: [
 | 
			
		||||
                          for (var idx = 0; idx < attachments.length; idx++)
 | 
			
		||||
                            SizedBox(
 | 
			
		||||
                              width: 120,
 | 
			
		||||
                              height: 120,
 | 
			
		||||
                              width: 280,
 | 
			
		||||
                              height: 280,
 | 
			
		||||
                              child: AttachmentPreview(
 | 
			
		||||
                                item: attachments[idx],
 | 
			
		||||
                                progress: progressMap[idx],
 | 
			
		||||
@@ -358,6 +365,12 @@ class ArticleComposeScreen extends HookConsumerWidget {
 | 
			
		||||
                                    delta,
 | 
			
		||||
                                  );
 | 
			
		||||
                                },
 | 
			
		||||
                                onInsert:
 | 
			
		||||
                                    () => ComposeLogic.insertAttachment(
 | 
			
		||||
                                      ref,
 | 
			
		||||
                                      state,
 | 
			
		||||
                                      idx,
 | 
			
		||||
                                    ),
 | 
			
		||||
                              ),
 | 
			
		||||
                            ),
 | 
			
		||||
                        ],
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/post.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
import 'package:island/widgets/app_scaffold.dart';
 | 
			
		||||
import 'package:island/widgets/post/post_item.dart';
 | 
			
		||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
 | 
			
		||||
 | 
			
		||||
@@ -107,7 +108,7 @@ class _PostSearchScreenState extends ConsumerState<PostSearchScreen> {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        title: TextField(
 | 
			
		||||
          controller: _searchController,
 | 
			
		||||
 
 | 
			
		||||
@@ -187,7 +187,7 @@ class PublisherProfileScreen extends HookConsumerWidget {
 | 
			
		||||
                        ),
 | 
			
		||||
                        onTap: () {
 | 
			
		||||
                          Navigator.pop(context, true);
 | 
			
		||||
                          context.push('/account/${data.name}');
 | 
			
		||||
                          context.push('/account/${data.account?.name}');
 | 
			
		||||
                        },
 | 
			
		||||
                      ),
 | 
			
		||||
                      Expanded(
 | 
			
		||||
 
 | 
			
		||||
@@ -77,6 +77,7 @@ class RealmDetailScreen extends HookConsumerWidget {
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      noBackground: false,
 | 
			
		||||
      body: realmState.when(
 | 
			
		||||
        loading: () => const Center(child: CircularProgressIndicator()),
 | 
			
		||||
        error: (error, _) => Center(child: Text('Error: $error')),
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										105
									
								
								lib/screens/reports/report_detail.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								lib/screens/reports/report_detail.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/abuse_report.dart';
 | 
			
		||||
import 'package:island/models/abuse_report_type.dart';
 | 
			
		||||
import 'package:island/services/abuse_report_service.dart';
 | 
			
		||||
import 'package:island/widgets/app_scaffold.dart';
 | 
			
		||||
import 'package:styled_widget/styled_widget.dart';
 | 
			
		||||
 | 
			
		||||
class AbuseReportDetailScreen extends ConsumerStatefulWidget {
 | 
			
		||||
  final String reportId;
 | 
			
		||||
 | 
			
		||||
  const AbuseReportDetailScreen({super.key, required this.reportId});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  ConsumerState<AbuseReportDetailScreen> createState() =>
 | 
			
		||||
      _AbuseReportDetailScreenState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _AbuseReportDetailScreenState
 | 
			
		||||
    extends ConsumerState<AbuseReportDetailScreen> {
 | 
			
		||||
  Future<SnAbuseReport>? _reportFuture;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    super.initState();
 | 
			
		||||
    _reportFuture = ref
 | 
			
		||||
        .read(abuseReportServiceProvider)
 | 
			
		||||
        .getReport(widget.reportId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      appBar: AppBar(title: const Text('Abuse Report Details')),
 | 
			
		||||
      body: FutureBuilder<SnAbuseReport>(
 | 
			
		||||
        future: _reportFuture,
 | 
			
		||||
        builder: (context, snapshot) {
 | 
			
		||||
          if (snapshot.connectionState == ConnectionState.waiting) {
 | 
			
		||||
            return const Center(child: CircularProgressIndicator());
 | 
			
		||||
          } else if (snapshot.hasError) {
 | 
			
		||||
            return Center(child: Text('Error: ${snapshot.error}'));
 | 
			
		||||
          } else if (snapshot.hasData) {
 | 
			
		||||
            final report = snapshot.data!;
 | 
			
		||||
            return Padding(
 | 
			
		||||
              padding: const EdgeInsets.all(16.0),
 | 
			
		||||
              child: Column(
 | 
			
		||||
                crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                children: [
 | 
			
		||||
                  _buildDetailRow(context, 'Report ID', report.id),
 | 
			
		||||
                  _buildDetailRow(
 | 
			
		||||
                    context,
 | 
			
		||||
                    'Resource Identifier',
 | 
			
		||||
                    report.resourceIdentifier,
 | 
			
		||||
                  ),
 | 
			
		||||
                  _buildDetailRow(
 | 
			
		||||
                    context,
 | 
			
		||||
                    'Type',
 | 
			
		||||
                    AbuseReportType.fromValue(report.type).displayName,
 | 
			
		||||
                  ),
 | 
			
		||||
                  _buildDetailRow(context, 'Reason', report.reason),
 | 
			
		||||
                  _buildDetailRow(
 | 
			
		||||
                    context,
 | 
			
		||||
                    'Resolved At',
 | 
			
		||||
                    report.resolvedAt?.toString() ?? 'N/A',
 | 
			
		||||
                  ),
 | 
			
		||||
                  _buildDetailRow(
 | 
			
		||||
                    context,
 | 
			
		||||
                    'Resolution',
 | 
			
		||||
                    report.resolution ?? 'N/A',
 | 
			
		||||
                  ),
 | 
			
		||||
                  _buildDetailRow(context, 'Account ID', report.accountId),
 | 
			
		||||
                  _buildDetailRow(
 | 
			
		||||
                    context,
 | 
			
		||||
                    'Created At',
 | 
			
		||||
                    report.createdAt.toString(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  _buildDetailRow(
 | 
			
		||||
                    context,
 | 
			
		||||
                    'Updated At',
 | 
			
		||||
                    report.updatedAt.toString(),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
            );
 | 
			
		||||
          } else {
 | 
			
		||||
            return const Center(child: Text('No data'));
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget _buildDetailRow(BuildContext context, String label, String value) {
 | 
			
		||||
    return Padding(
 | 
			
		||||
      padding: const EdgeInsets.symmetric(vertical: 8.0),
 | 
			
		||||
      child: Column(
 | 
			
		||||
        crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
        children: [
 | 
			
		||||
          Text(label, style: Theme.of(context).textTheme.titleMedium).bold(),
 | 
			
		||||
          Text(value, style: Theme.of(context).textTheme.bodyLarge),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										153
									
								
								lib/screens/reports/report_list.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								lib/screens/reports/report_list.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,153 @@
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:go_router/go_router.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/abuse_report.dart';
 | 
			
		||||
import 'package:island/models/abuse_report_type.dart';
 | 
			
		||||
import 'package:island/services/abuse_report_service.dart';
 | 
			
		||||
import 'package:island/services/time.dart';
 | 
			
		||||
import 'package:island/widgets/app_scaffold.dart';
 | 
			
		||||
import 'package:island/widgets/safety/abuse_report_helper.dart';
 | 
			
		||||
 | 
			
		||||
class AbuseReportListScreen extends ConsumerStatefulWidget {
 | 
			
		||||
  const AbuseReportListScreen({super.key});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  ConsumerState<AbuseReportListScreen> createState() =>
 | 
			
		||||
      _AbuseReportListScreenState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _AbuseReportListScreenState extends ConsumerState<AbuseReportListScreen> {
 | 
			
		||||
  Future<List<SnAbuseReport>>? _reportsFuture;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    super.initState();
 | 
			
		||||
    _reportsFuture = ref.read(abuseReportServiceProvider).getReports();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      appBar: AppBar(title: Text('abuseReports').tr()),
 | 
			
		||||
      floatingActionButton: FloatingActionButton(
 | 
			
		||||
        child: const Icon(Icons.add),
 | 
			
		||||
        onPressed: () {
 | 
			
		||||
          showAbuseReportSheet(context, resourceIdentifier: 'unidentified');
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
      body: FutureBuilder<List<SnAbuseReport>>(
 | 
			
		||||
        future: _reportsFuture,
 | 
			
		||||
        builder: (context, snapshot) {
 | 
			
		||||
          if (snapshot.connectionState == ConnectionState.waiting) {
 | 
			
		||||
            return const Center(child: CircularProgressIndicator());
 | 
			
		||||
          } else if (snapshot.hasError) {
 | 
			
		||||
            return Center(child: Text('Error: ${snapshot.error}'));
 | 
			
		||||
          } else if (snapshot.hasData) {
 | 
			
		||||
            final reports = snapshot.data!;
 | 
			
		||||
            return ListView.builder(
 | 
			
		||||
              padding: EdgeInsets.zero,
 | 
			
		||||
              itemCount: reports.length,
 | 
			
		||||
              itemBuilder: (context, index) {
 | 
			
		||||
                final report = reports[index];
 | 
			
		||||
                return Card(
 | 
			
		||||
                  elevation: 2,
 | 
			
		||||
                  margin: const EdgeInsets.symmetric(
 | 
			
		||||
                    horizontal: 16,
 | 
			
		||||
                    vertical: 8,
 | 
			
		||||
                  ),
 | 
			
		||||
                  child: InkWell(
 | 
			
		||||
                    onTap: () {
 | 
			
		||||
                      context.push('/safety/reports/me/${report.id}');
 | 
			
		||||
                    },
 | 
			
		||||
                    child: Padding(
 | 
			
		||||
                      padding: const EdgeInsets.all(16.0),
 | 
			
		||||
                      child: Column(
 | 
			
		||||
                        crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                        children: [
 | 
			
		||||
                          Text(
 | 
			
		||||
                            report.reason,
 | 
			
		||||
                            style: Theme.of(context).textTheme.titleMedium,
 | 
			
		||||
                          ),
 | 
			
		||||
                          const SizedBox(height: 8),
 | 
			
		||||
                          Row(
 | 
			
		||||
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
			
		||||
                            children: [
 | 
			
		||||
                              Text(
 | 
			
		||||
                                'ID',
 | 
			
		||||
                                style: Theme.of(context).textTheme.bodySmall,
 | 
			
		||||
                              ),
 | 
			
		||||
                              Text(
 | 
			
		||||
                                report.id,
 | 
			
		||||
                                style: Theme.of(context).textTheme.bodyMedium,
 | 
			
		||||
                              ),
 | 
			
		||||
                            ],
 | 
			
		||||
                          ),
 | 
			
		||||
                          const SizedBox(height: 4),
 | 
			
		||||
                          Row(
 | 
			
		||||
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
			
		||||
                            children: [
 | 
			
		||||
                              Text(
 | 
			
		||||
                                'Type',
 | 
			
		||||
                                style: Theme.of(context).textTheme.bodySmall,
 | 
			
		||||
                              ),
 | 
			
		||||
                              Text(
 | 
			
		||||
                                AbuseReportType.fromValue(
 | 
			
		||||
                                  report.type,
 | 
			
		||||
                                ).displayName,
 | 
			
		||||
                                style: Theme.of(context).textTheme.bodyMedium,
 | 
			
		||||
                              ),
 | 
			
		||||
                            ],
 | 
			
		||||
                          ),
 | 
			
		||||
                          const SizedBox(height: 4),
 | 
			
		||||
                          Row(
 | 
			
		||||
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
			
		||||
                            children: [
 | 
			
		||||
                              Text(
 | 
			
		||||
                                'Created at',
 | 
			
		||||
                                style: Theme.of(context).textTheme.bodySmall,
 | 
			
		||||
                              ),
 | 
			
		||||
                              Text(
 | 
			
		||||
                                '${report.createdAt.formatRelative(context)} · ${report.createdAt.formatSystem()}',
 | 
			
		||||
                                style: Theme.of(context).textTheme.bodyMedium,
 | 
			
		||||
                              ),
 | 
			
		||||
                            ],
 | 
			
		||||
                          ),
 | 
			
		||||
                          const SizedBox(height: 4),
 | 
			
		||||
                          Row(
 | 
			
		||||
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
			
		||||
                            children: [
 | 
			
		||||
                              Text(
 | 
			
		||||
                                'Status',
 | 
			
		||||
                                style: Theme.of(context).textTheme.bodySmall,
 | 
			
		||||
                              ),
 | 
			
		||||
                              Text(
 | 
			
		||||
                                report.resolvedAt != null
 | 
			
		||||
                                    ? 'Resolved'
 | 
			
		||||
                                    : 'Unresolved',
 | 
			
		||||
                                style: Theme.of(
 | 
			
		||||
                                  context,
 | 
			
		||||
                                ).textTheme.bodyMedium?.copyWith(
 | 
			
		||||
                                  color:
 | 
			
		||||
                                      report.resolvedAt != null
 | 
			
		||||
                                          ? Colors.green
 | 
			
		||||
                                          : Colors.orange,
 | 
			
		||||
                                ),
 | 
			
		||||
                              ),
 | 
			
		||||
                            ],
 | 
			
		||||
                          ),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                );
 | 
			
		||||
              },
 | 
			
		||||
            );
 | 
			
		||||
          } else {
 | 
			
		||||
            return const Center(child: Text('No data'));
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								lib/services/abuse_report_service.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								lib/services/abuse_report_service.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/abuse_report.dart';
 | 
			
		||||
import 'package:island/pods/network.dart';
 | 
			
		||||
 | 
			
		||||
final abuseReportServiceProvider = Provider<AbuseReportService>((ref) {
 | 
			
		||||
  return AbuseReportService(ref);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
class AbuseReportService {
 | 
			
		||||
  final Ref ref;
 | 
			
		||||
  AbuseReportService(this.ref);
 | 
			
		||||
 | 
			
		||||
  Future<SnAbuseReport> getReport(String id) async {
 | 
			
		||||
    final response =
 | 
			
		||||
        await ref.read(apiClientProvider).get('/safety/reports/me/$id');
 | 
			
		||||
    return SnAbuseReport.fromJson(response.data);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<List<SnAbuseReport>> getReports() async {
 | 
			
		||||
    final response = await ref.read(apiClientProvider).get('/safety/reports/me');
 | 
			
		||||
    return (response.data as List)
 | 
			
		||||
        .map((json) => SnAbuseReport.fromJson(json))
 | 
			
		||||
        .toList();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -68,7 +68,6 @@ Future<void> subscribePushNotification(
 | 
			
		||||
  bool detailedErrors = false,
 | 
			
		||||
}) async {
 | 
			
		||||
  await FirebaseMessaging.instance.requestPermission(
 | 
			
		||||
    provisional: true,
 | 
			
		||||
    alert: true,
 | 
			
		||||
    badge: true,
 | 
			
		||||
    sound: true,
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@ import 'dart:convert';
 | 
			
		||||
import 'package:flutter/widgets.dart';
 | 
			
		||||
import 'package:freezed_annotation/freezed_annotation.dart';
 | 
			
		||||
import 'package:island/pods/config.dart';
 | 
			
		||||
import 'package:island/widgets/tour/techincal_review_intro.dart';
 | 
			
		||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
			
		||||
 | 
			
		||||
part 'tour.g.dart';
 | 
			
		||||
@@ -12,7 +11,7 @@ part 'tour.freezed.dart';
 | 
			
		||||
const kAppTourStatusKey = "app_tour_statuses";
 | 
			
		||||
 | 
			
		||||
const List<Tour> kAllTours = [
 | 
			
		||||
  Tour(id: 'technical_review_intro', isStartup: true),
 | 
			
		||||
  // Tour(id: 'technical_review_intro', isStartup: true),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@freezed
 | 
			
		||||
@@ -22,7 +21,7 @@ sealed class Tour with _$Tour {
 | 
			
		||||
  const factory Tour({required String id, required bool isStartup}) = _Tour;
 | 
			
		||||
 | 
			
		||||
  Widget get widget => switch (id) {
 | 
			
		||||
    'technical_review_intro' => const TechicalReviewIntroWidget(),
 | 
			
		||||
    // 'technical_review_intro' => const TechicalReviewIntroWidget(),
 | 
			
		||||
    _ => throw UnimplementedError(),
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								lib/utils/abuse_report_utils.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								lib/utils/abuse_report_utils.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
String getAbuseReportTypeString(int type) {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case 0:
 | 
			
		||||
      return 'Copyright';
 | 
			
		||||
    case 1:
 | 
			
		||||
      return 'Harassment';
 | 
			
		||||
    case 2:
 | 
			
		||||
      return 'Impersonation';
 | 
			
		||||
    case 3:
 | 
			
		||||
      return 'Offensive Content';
 | 
			
		||||
    case 4:
 | 
			
		||||
      return 'Spam';
 | 
			
		||||
    case 5:
 | 
			
		||||
      return 'Privacy Violation';
 | 
			
		||||
    case 6:
 | 
			
		||||
      return 'Illegal Content';
 | 
			
		||||
    case 7:
 | 
			
		||||
      return 'Other';
 | 
			
		||||
    default:
 | 
			
		||||
      return 'Unknown';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -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/$roomId/call');
 | 
			
		||||
        }
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        showErrorAlert(e);
 | 
			
		||||
 
 | 
			
		||||
@@ -360,7 +360,7 @@ class CallOverlayBar extends HookConsumerWidget {
 | 
			
		||||
        ).padding(all: 16),
 | 
			
		||||
      ),
 | 
			
		||||
      onTap: () {
 | 
			
		||||
        context.push('/chat/call/${callNotifier.roomId!}');
 | 
			
		||||
        context.push('/chat/${callNotifier.roomId!}/call');
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ class AttachmentPreview extends StatelessWidget {
 | 
			
		||||
  final double? progress;
 | 
			
		||||
  final Function(int)? onMove;
 | 
			
		||||
  final Function? onDelete;
 | 
			
		||||
  final Function? onInsert;
 | 
			
		||||
  final Function? onRequestUpload;
 | 
			
		||||
  const AttachmentPreview({
 | 
			
		||||
    super.key,
 | 
			
		||||
@@ -23,13 +24,17 @@ class AttachmentPreview extends StatelessWidget {
 | 
			
		||||
    this.onRequestUpload,
 | 
			
		||||
    this.onMove,
 | 
			
		||||
    this.onDelete,
 | 
			
		||||
    this.onInsert,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    var ratio =
 | 
			
		||||
        (item.isOnCloud ? (item.data.fileMeta?['ratio'] ?? 1) : 1).toDouble();
 | 
			
		||||
    if (ratio == 0) ratio = 1.0;
 | 
			
		||||
 | 
			
		||||
    return AspectRatio(
 | 
			
		||||
      aspectRatio:
 | 
			
		||||
          (item.isOnCloud ? (item.data.fileMeta?['ratio'] ?? 1) : 1).toDouble(),
 | 
			
		||||
      aspectRatio: ratio,
 | 
			
		||||
      child: ClipRRect(
 | 
			
		||||
        borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        child: Stack(
 | 
			
		||||
@@ -104,7 +109,11 @@ class AttachmentPreview extends StatelessWidget {
 | 
			
		||||
                          style: TextStyle(color: Colors.white),
 | 
			
		||||
                        ),
 | 
			
		||||
                      Gap(6),
 | 
			
		||||
                      Center(child: LinearProgressIndicator(value: progress)),
 | 
			
		||||
                      Center(
 | 
			
		||||
                        child: LinearProgressIndicator(
 | 
			
		||||
                          value: progress != null ? progress! / 100.0 : null,
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
@@ -166,6 +175,18 @@ class AttachmentPreview extends StatelessWidget {
 | 
			
		||||
                              onMove?.call(1);
 | 
			
		||||
                            },
 | 
			
		||||
                          ),
 | 
			
		||||
                        if (onInsert != null)
 | 
			
		||||
                          InkWell(
 | 
			
		||||
                            borderRadius: BorderRadius.circular(8),
 | 
			
		||||
                            child: const Icon(
 | 
			
		||||
                              Symbols.add,
 | 
			
		||||
                              size: 14,
 | 
			
		||||
                              color: Colors.white,
 | 
			
		||||
                            ).padding(horizontal: 8, vertical: 6),
 | 
			
		||||
                            onTap: () {
 | 
			
		||||
                              onInsert?.call();
 | 
			
		||||
                            },
 | 
			
		||||
                          ),
 | 
			
		||||
                      ],
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
 
 | 
			
		||||
@@ -37,13 +37,10 @@ class CloudFileList extends HookConsumerWidget {
 | 
			
		||||
 | 
			
		||||
  double calculateAspectRatio() {
 | 
			
		||||
    double total = 0;
 | 
			
		||||
    for (var ratio in files.map(
 | 
			
		||||
      (e) =>
 | 
			
		||||
          e.fileMeta?['ratio'] ??
 | 
			
		||||
          ((e.mimeType?.startsWith('image') ?? false) ? 1 : 16 / 9),
 | 
			
		||||
    )) {
 | 
			
		||||
    for (var ratio in files.map((e) => e.fileMeta?['ratio'] ?? 1)) {
 | 
			
		||||
      total += ratio;
 | 
			
		||||
    }
 | 
			
		||||
    if (total == 0) return 1;
 | 
			
		||||
    return total / files.length;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,22 +25,21 @@ class CloudFileWidget extends ConsumerWidget {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    final serverUrl = ref.watch(serverUrlProvider);
 | 
			
		||||
    final uri = '$serverUrl/files/${item.id}';
 | 
			
		||||
    final uri = '$serverUrl/api/files/${item.id}';
 | 
			
		||||
 | 
			
		||||
    var ratio = (item.fileMeta?['ratio'] ?? 1).toDouble();
 | 
			
		||||
    if (ratio == 0) ratio = 1.0;
 | 
			
		||||
    final content = switch (item.mimeType?.split('/').firstOrNull) {
 | 
			
		||||
      "image" => AspectRatio(
 | 
			
		||||
        aspectRatio: (item.fileMeta?['ratio'] ?? 1).toDouble(),
 | 
			
		||||
        aspectRatio: ratio,
 | 
			
		||||
        child: UniversalImage(
 | 
			
		||||
          uri: uri,
 | 
			
		||||
          blurHash: noBlurhash ? null : item.fileMeta?['blur'],
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
      "video" => AspectRatio(
 | 
			
		||||
        aspectRatio: (item.fileMeta?['ratio'] ?? 16 / 9).toDouble(),
 | 
			
		||||
        child: UniversalVideo(
 | 
			
		||||
          uri: uri,
 | 
			
		||||
          aspectRatio: (item.fileMeta?['ratio'] ?? 16 / 9).toDouble(),
 | 
			
		||||
        ),
 | 
			
		||||
        aspectRatio: ratio,
 | 
			
		||||
        child: UniversalVideo(uri: uri, aspectRatio: ratio),
 | 
			
		||||
      ),
 | 
			
		||||
      _ => Text('Unable render for ${item.mimeType}'),
 | 
			
		||||
    };
 | 
			
		||||
@@ -71,7 +70,7 @@ class CloudImageWidget extends ConsumerWidget {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    final serverUrl = ref.watch(serverUrlProvider);
 | 
			
		||||
    final uri = '$serverUrl/files/${file?.id ?? fileId}';
 | 
			
		||||
    final uri = '$serverUrl/api/files/${file?.id ?? fileId}';
 | 
			
		||||
 | 
			
		||||
    return AspectRatio(
 | 
			
		||||
      aspectRatio: aspectRatio,
 | 
			
		||||
@@ -87,7 +86,7 @@ class CloudImageWidget extends ConsumerWidget {
 | 
			
		||||
    required String serverUrl,
 | 
			
		||||
    bool original = false,
 | 
			
		||||
  }) {
 | 
			
		||||
    final uri = '$serverUrl/files/$fileId?original=$original';
 | 
			
		||||
    final uri = '$serverUrl/api/files/$fileId?original=$original';
 | 
			
		||||
    return CachedNetworkImageProvider(uri);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -110,7 +109,7 @@ class ProfilePictureWidget extends ConsumerWidget {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    final serverUrl = ref.watch(serverUrlProvider);
 | 
			
		||||
    final uri = '$serverUrl/files/${file?.id ?? fileId}';
 | 
			
		||||
    final uri = '$serverUrl/api/files/${file?.id ?? fileId}';
 | 
			
		||||
 | 
			
		||||
    return ClipRRect(
 | 
			
		||||
      borderRadius: BorderRadius.all(Radius.circular(radius)),
 | 
			
		||||
@@ -303,7 +302,7 @@ class SplitAvatarWidget extends ConsumerWidget {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final serverUrl = ref.watch(serverUrlProvider);
 | 
			
		||||
    final uri = '$serverUrl/files/$fileId';
 | 
			
		||||
    final uri = '$serverUrl/api/files/$fileId';
 | 
			
		||||
 | 
			
		||||
    return SizedBox(
 | 
			
		||||
      width: radius,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import 'package:collection/collection.dart';
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:go_router/go_router.dart';
 | 
			
		||||
@@ -6,11 +7,14 @@ import 'package:flutter_highlight/themes/a11y-dark.dart';
 | 
			
		||||
import 'package:flutter_highlight/themes/a11y-light.dart';
 | 
			
		||||
import 'package:flutter_hooks/flutter_hooks.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/file.dart';
 | 
			
		||||
import 'package:island/pods/config.dart';
 | 
			
		||||
import 'package:island/widgets/alert.dart';
 | 
			
		||||
import 'package:island/widgets/content/cloud_files.dart';
 | 
			
		||||
import 'package:island/widgets/content/markdown_latex.dart';
 | 
			
		||||
import 'package:markdown/markdown.dart' as markdown;
 | 
			
		||||
import 'package:markdown_widget/markdown_widget.dart';
 | 
			
		||||
import 'package:styled_widget/styled_widget.dart';
 | 
			
		||||
import 'package:url_launcher/url_launcher.dart';
 | 
			
		||||
 | 
			
		||||
import 'image.dart';
 | 
			
		||||
@@ -23,6 +27,7 @@ class MarkdownTextContent extends HookConsumerWidget {
 | 
			
		||||
  final TextStyle? linkStyle;
 | 
			
		||||
  final EdgeInsets? linesMargin;
 | 
			
		||||
  final bool isSelectable;
 | 
			
		||||
  final List<SnCloudFile>? attachments;
 | 
			
		||||
 | 
			
		||||
  const MarkdownTextContent({
 | 
			
		||||
    super.key,
 | 
			
		||||
@@ -33,6 +38,7 @@ class MarkdownTextContent extends HookConsumerWidget {
 | 
			
		||||
    this.linkStyle,
 | 
			
		||||
    this.isSelectable = false,
 | 
			
		||||
    this.linesMargin,
 | 
			
		||||
    this.attachments,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
@@ -109,6 +115,29 @@ class MarkdownTextContent extends HookConsumerWidget {
 | 
			
		||||
              final uri = Uri.parse(url);
 | 
			
		||||
              if (uri.scheme == 'solian') {
 | 
			
		||||
                switch (uri.host) {
 | 
			
		||||
                  case 'files':
 | 
			
		||||
                    final file = attachments?.firstWhereOrNull(
 | 
			
		||||
                      (file) => file.id == uri.pathSegments[0],
 | 
			
		||||
                    );
 | 
			
		||||
                    if (file == null) {
 | 
			
		||||
                      return const SizedBox.shrink();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return ClipRRect(
 | 
			
		||||
                      borderRadius: const BorderRadius.all(Radius.circular(8)),
 | 
			
		||||
                      child: Container(
 | 
			
		||||
                        decoration: BoxDecoration(
 | 
			
		||||
                          color: Theme.of(context).colorScheme.surfaceContainer,
 | 
			
		||||
                          borderRadius: const BorderRadius.all(
 | 
			
		||||
                            Radius.circular(8),
 | 
			
		||||
                          ),
 | 
			
		||||
                        ),
 | 
			
		||||
                        child: CloudFileWidget(
 | 
			
		||||
                          item: file,
 | 
			
		||||
                          fit: BoxFit.cover,
 | 
			
		||||
                        ).clipRRect(all: 8),
 | 
			
		||||
                      ),
 | 
			
		||||
                    );
 | 
			
		||||
                  case 'stickers':
 | 
			
		||||
                    final size = doesEnlargeSticker ? 96.0 : 24.0;
 | 
			
		||||
                    return ClipRRect(
 | 
			
		||||
@@ -132,9 +161,9 @@ class MarkdownTextContent extends HookConsumerWidget {
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
              final content = UniversalImage(
 | 
			
		||||
                uri: uri.toString(),
 | 
			
		||||
                fit: BoxFit.cover,
 | 
			
		||||
              final content = ConstrainedBox(
 | 
			
		||||
                constraints: BoxConstraints(maxHeight: 360),
 | 
			
		||||
                child: UniversalImage(uri: uri.toString(), fit: BoxFit.contain),
 | 
			
		||||
              );
 | 
			
		||||
              return content;
 | 
			
		||||
            },
 | 
			
		||||
 
 | 
			
		||||
@@ -70,7 +70,7 @@ class _UniversalVideoState extends ConsumerState<UniversalVideo> {
 | 
			
		||||
 | 
			
		||||
    return Video(
 | 
			
		||||
      controller: _videoController!,
 | 
			
		||||
      aspectRatio: widget.aspectRatio,
 | 
			
		||||
      aspectRatio: widget.aspectRatio != 1 ? widget.aspectRatio : null,
 | 
			
		||||
      controls:
 | 
			
		||||
          !kIsWeb && (Platform.isAndroid || Platform.isIOS)
 | 
			
		||||
              ? MaterialVideoControls
 | 
			
		||||
 
 | 
			
		||||
@@ -474,6 +474,23 @@ class ComposeLogic {
 | 
			
		||||
    state.attachments.value = clone;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static void insertAttachment(WidgetRef ref, ComposeState state, int index) {
 | 
			
		||||
    final attachment = state.attachments.value[index];
 | 
			
		||||
    if (!attachment.isOnCloud) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    final cloudFile = attachment.data as SnCloudFile;
 | 
			
		||||
    final markdown = '';
 | 
			
		||||
    final controller = state.contentController;
 | 
			
		||||
    final text = controller.text;
 | 
			
		||||
    final selection = controller.selection;
 | 
			
		||||
    final newText = text.replaceRange(selection.start, selection.end, markdown);
 | 
			
		||||
    controller.text = newText;
 | 
			
		||||
    controller.selection = TextSelection.fromPosition(
 | 
			
		||||
      TextPosition(offset: selection.start + markdown.length),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static Future<void> performAction(
 | 
			
		||||
    WidgetRef ref,
 | 
			
		||||
    ComposeState state,
 | 
			
		||||
 
 | 
			
		||||
@@ -163,7 +163,7 @@ class PostItem extends HookConsumerWidget {
 | 
			
		||||
            if ((item.repliedPost != null || item.forwardedPost != null) &&
 | 
			
		||||
                showReferencePost)
 | 
			
		||||
              _buildReferencePost(context, item),
 | 
			
		||||
            if (item.attachments.isNotEmpty)
 | 
			
		||||
            if (item.attachments.isNotEmpty && item.type != 1)
 | 
			
		||||
              CloudFileList(
 | 
			
		||||
                files: item.attachments,
 | 
			
		||||
                maxWidth: math.min(
 | 
			
		||||
@@ -331,6 +331,7 @@ class PostItem extends HookConsumerWidget {
 | 
			
		||||
                                  item.type == 0
 | 
			
		||||
                                      ? EdgeInsets.only(bottom: 8)
 | 
			
		||||
                                      : null,
 | 
			
		||||
                              attachments: item.attachments,
 | 
			
		||||
                            ),
 | 
			
		||||
                        ],
 | 
			
		||||
                        // Render tags and categories if they exist
 | 
			
		||||
@@ -383,13 +384,18 @@ class PostItem extends HookConsumerWidget {
 | 
			
		||||
                        // Show truncation hint if post is truncated
 | 
			
		||||
                        if (item.isTruncated && !isFullPost && item.type != 1)
 | 
			
		||||
                          _PostTruncateHint().padding(
 | 
			
		||||
                            bottom: item.attachments.isNotEmpty ? 8 : null,
 | 
			
		||||
                            bottom:
 | 
			
		||||
                                (item.attachments.isNotEmpty ||
 | 
			
		||||
                                        item.repliedPost != null ||
 | 
			
		||||
                                        item.forwardedPost != null)
 | 
			
		||||
                                    ? 8
 | 
			
		||||
                                    : null,
 | 
			
		||||
                          ),
 | 
			
		||||
                        if ((item.repliedPost != null ||
 | 
			
		||||
                                item.forwardedPost != null) &&
 | 
			
		||||
                            showReferencePost)
 | 
			
		||||
                          _buildReferencePost(context, item),
 | 
			
		||||
                        if (item.attachments.isNotEmpty)
 | 
			
		||||
                        if (item.attachments.isNotEmpty && item.type != 1)
 | 
			
		||||
                          CloudFileList(
 | 
			
		||||
                            files: item.attachments,
 | 
			
		||||
                            maxWidth: math.min(
 | 
			
		||||
@@ -570,7 +576,7 @@ class PostItem extends HookConsumerWidget {
 | 
			
		||||
              callback: () {
 | 
			
		||||
                showAbuseReportSheet(
 | 
			
		||||
                  context,
 | 
			
		||||
                  resourceIdentifier: 'posts:${item.id}',
 | 
			
		||||
                  resourceIdentifier: 'post/${item.id}',
 | 
			
		||||
                );
 | 
			
		||||
              },
 | 
			
		||||
            ),
 | 
			
		||||
@@ -689,6 +695,7 @@ Widget _buildReferencePost(BuildContext context, SnPost item) {
 | 
			
		||||
                          referencePost.type == 0
 | 
			
		||||
                              ? EdgeInsets.only(bottom: 4)
 | 
			
		||||
                              : null,
 | 
			
		||||
                      attachments: item.attachments,
 | 
			
		||||
                    ).padding(bottom: 4),
 | 
			
		||||
                  // Truncation hint for referenced post
 | 
			
		||||
                  if (referencePost.isTruncated)
 | 
			
		||||
@@ -696,7 +703,8 @@ Widget _buildReferencePost(BuildContext context, SnPost item) {
 | 
			
		||||
                      isCompact: true,
 | 
			
		||||
                      margin: const EdgeInsets.only(top: 4, bottom: 8),
 | 
			
		||||
                    ),
 | 
			
		||||
                  if (referencePost.attachments.isNotEmpty)
 | 
			
		||||
                  if (referencePost.attachments.isNotEmpty &&
 | 
			
		||||
                      referencePost.type != 1)
 | 
			
		||||
                    Row(
 | 
			
		||||
                      mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                      children: [
 | 
			
		||||
@@ -1030,6 +1038,7 @@ class _ArticlePostDisplay extends StatelessWidget {
 | 
			
		||||
            MarkdownTextContent(
 | 
			
		||||
              content: item.content!,
 | 
			
		||||
              textStyle: Theme.of(context).textTheme.bodyLarge,
 | 
			
		||||
              attachments: item.attachments,
 | 
			
		||||
            ),
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:island/models/post.dart';
 | 
			
		||||
import 'package:island/pods/userinfo.dart';
 | 
			
		||||
import 'package:island/widgets/content/sheet.dart';
 | 
			
		||||
import 'package:island/widgets/post/post_replies.dart';
 | 
			
		||||
import 'package:island/widgets/post/post_quick_reply.dart';
 | 
			
		||||
@@ -14,6 +15,8 @@ class PostRepliesSheet extends HookConsumerWidget {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    final user = ref.watch(userInfoProvider);
 | 
			
		||||
 | 
			
		||||
    return SheetScaffold(
 | 
			
		||||
      titleText: 'repliesCount'.plural(post.repliesCount),
 | 
			
		||||
      child: Column(
 | 
			
		||||
@@ -21,26 +24,29 @@ class PostRepliesSheet extends HookConsumerWidget {
 | 
			
		||||
          // Replies list
 | 
			
		||||
          Expanded(
 | 
			
		||||
            child: CustomScrollView(
 | 
			
		||||
              slivers: [PostRepliesList(
 | 
			
		||||
                postId: post.id.toString(),
 | 
			
		||||
                backgroundColor: Colors.transparent,
 | 
			
		||||
              )],
 | 
			
		||||
              slivers: [
 | 
			
		||||
                PostRepliesList(
 | 
			
		||||
                  postId: post.id.toString(),
 | 
			
		||||
                  backgroundColor: Colors.transparent,
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          // Quick reply section
 | 
			
		||||
          Material(
 | 
			
		||||
            elevation: 2,
 | 
			
		||||
            child: PostQuickReply(
 | 
			
		||||
              parent: post,
 | 
			
		||||
              onPosted: () {
 | 
			
		||||
                ref.invalidate(postRepliesNotifierProvider(post.id));
 | 
			
		||||
              },
 | 
			
		||||
            ).padding(
 | 
			
		||||
              bottom: MediaQuery.of(context).padding.bottom + 16,
 | 
			
		||||
              top: 16,
 | 
			
		||||
              horizontal: 16,
 | 
			
		||||
          if (user.value != null)
 | 
			
		||||
            Material(
 | 
			
		||||
              elevation: 2,
 | 
			
		||||
              child: PostQuickReply(
 | 
			
		||||
                parent: post,
 | 
			
		||||
                onPosted: () {
 | 
			
		||||
                  ref.invalidate(postRepliesNotifierProvider(post.id));
 | 
			
		||||
                },
 | 
			
		||||
              ).padding(
 | 
			
		||||
                bottom: MediaQuery.of(context).padding.bottom + 16,
 | 
			
		||||
                top: 16,
 | 
			
		||||
                horizontal: 16,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
 | 
			
		||||
  GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin"))
 | 
			
		||||
  IrondashEngineContextPlugin.register(with: registry.registrar(forPlugin: "IrondashEngineContextPlugin"))
 | 
			
		||||
  LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin"))
 | 
			
		||||
  FLALocalAuthPlugin.register(with: registry.registrar(forPlugin: "FLALocalAuthPlugin"))
 | 
			
		||||
  LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin"))
 | 
			
		||||
  MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
 | 
			
		||||
  MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
 | 
			
		||||
  FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
 | 
			
		||||
 
 | 
			
		||||
@@ -16,10 +16,10 @@ PODS:
 | 
			
		||||
  - Firebase/Messaging (11.15.0):
 | 
			
		||||
    - Firebase/CoreOnly
 | 
			
		||||
    - FirebaseMessaging (~> 11.15.0)
 | 
			
		||||
  - firebase_core (3.15.0):
 | 
			
		||||
  - firebase_core (3.15.1):
 | 
			
		||||
    - Firebase/CoreOnly (~> 11.15.0)
 | 
			
		||||
    - FlutterMacOS
 | 
			
		||||
  - firebase_messaging (15.2.8):
 | 
			
		||||
  - firebase_messaging (15.2.9):
 | 
			
		||||
    - Firebase/CoreOnly (~> 11.15.0)
 | 
			
		||||
    - Firebase/Messaging (~> 11.15.0)
 | 
			
		||||
    - firebase_core
 | 
			
		||||
@@ -292,8 +292,8 @@ SPEC CHECKSUMS:
 | 
			
		||||
  file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
 | 
			
		||||
  file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31
 | 
			
		||||
  Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
 | 
			
		||||
  firebase_core: 177f51be1650b15d2d5b9f1abf48792619288070
 | 
			
		||||
  firebase_messaging: 8748a5d4bb435993cffa7f5501292f3e914a23d7
 | 
			
		||||
  firebase_core: 8dc569d17b3a9fc3ee5ebc21b322411b4a796833
 | 
			
		||||
  firebase_messaging: adaf7fc22897a7aa49410d15f8a595bef2dbca2d
 | 
			
		||||
  FirebaseCore: efb3893e5b94f32b86e331e3bd6dadf18b66568e
 | 
			
		||||
  FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4
 | 
			
		||||
  FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843
 | 
			
		||||
@@ -310,7 +310,7 @@ SPEC CHECKSUMS:
 | 
			
		||||
  GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
 | 
			
		||||
  irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba
 | 
			
		||||
  livekit_client: c9d9f41996f5cf22b9ba0e8483e6af4ca5094059
 | 
			
		||||
  local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
 | 
			
		||||
  local_auth_darwin: d2e8c53ef0c4f43c646462e3415432c4dab3ae19
 | 
			
		||||
  media_kit_libs_macos_video: 85a23e549b5f480e72cae3e5634b5514bc692f65
 | 
			
		||||
  media_kit_video: fa6564e3799a0a28bff39442334817088b7ca758
 | 
			
		||||
  nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										48
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								pubspec.lock
									
									
									
									
									
								
							@@ -13,10 +13,10 @@ packages:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: _flutterfire_internals
 | 
			
		||||
      sha256: "50e24b769bd1e725732f0aff18b806b8731c1fbcf4e8018ab98e7c4805a2a52f"
 | 
			
		||||
      sha256: a5788040810bd84400bc209913fbc40f388cded7cdf95ee2f5d2bff7e38d5241
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.3.57"
 | 
			
		||||
    version: "1.3.58"
 | 
			
		||||
  analyzer:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -349,10 +349,10 @@ packages:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: coverage
 | 
			
		||||
      sha256: aa07dbe5f2294c827b7edb9a87bba44a9c15a3cc81bc8da2ca19b37322d30080
 | 
			
		||||
      sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.14.1"
 | 
			
		||||
    version: "1.15.0"
 | 
			
		||||
  croppy:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@@ -629,50 +629,50 @@ packages:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_core
 | 
			
		||||
      sha256: "5bba5924139e91d26446fd2601c18a6aa62c1161c768a989bb5e245dcdc20644"
 | 
			
		||||
      sha256: c6e8a6bf883d8ddd0dec39be90872daca65beaa6f4cff0051ed3b16c56b82e9f
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "3.15.0"
 | 
			
		||||
    version: "3.15.1"
 | 
			
		||||
  firebase_core_platform_interface:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_core_platform_interface
 | 
			
		||||
      sha256: "5d2ab45779d91af2aa0252dec9fe4ee1caa015d83377de255454dcaa1526a0e0"
 | 
			
		||||
      sha256: "5dbc900677dcbe5873d22ad7fbd64b047750124f1f9b7ebe2a33b9ddccc838eb"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "5.4.1"
 | 
			
		||||
    version: "6.0.0"
 | 
			
		||||
  firebase_core_web:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_core_web
 | 
			
		||||
      sha256: eb3afccfc452b2b2075acbe0c4b27de62dd596802b4e5e19869c1e926cbb20b3
 | 
			
		||||
      sha256: "0ed0dc292e8f9ac50992e2394e9d336a0275b6ae400d64163fdf0a8a8b556c37"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.24.0"
 | 
			
		||||
    version: "2.24.1"
 | 
			
		||||
  firebase_messaging:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_messaging
 | 
			
		||||
      sha256: c6711cf2f455532b84a94022c7aaf85088849763af2f01b775ca79d82d10a01a
 | 
			
		||||
      sha256: "0f3363f97672eb9f65609fa00ed2f62cc8ec93e7e2d4def99726f9165d3d8a73"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "15.2.8"
 | 
			
		||||
    version: "15.2.9"
 | 
			
		||||
  firebase_messaging_platform_interface:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_messaging_platform_interface
 | 
			
		||||
      sha256: "1c9dacccb1aee1bf17ba519dda5563a16fdd2ec1e79b5f2e421cb4bf75a166f7"
 | 
			
		||||
      sha256: "7a05ef119a14c5f6a9440d1e0223bcba20c8daf555450e119c4c477bf2c3baa9"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "4.6.8"
 | 
			
		||||
    version: "4.6.9"
 | 
			
		||||
  firebase_messaging_web:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_messaging_web
 | 
			
		||||
      sha256: "54317c26fa92f0d90a2017977ac791cb0504eca29fcf397f06adf727d4a7a2d5"
 | 
			
		||||
      sha256: a4547f76da2a905190f899eb4d0150e1d0fd52206fce469d9f05ae15bb68b2c5
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "3.10.8"
 | 
			
		||||
    version: "3.10.9"
 | 
			
		||||
  fixnum:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -1049,18 +1049,18 @@ packages:
 | 
			
		||||
    dependency: "direct dev"
 | 
			
		||||
    description:
 | 
			
		||||
      name: freezed
 | 
			
		||||
      sha256: "6022db4c7bfa626841b2a10f34dd1e1b68e8f8f9650db6112dcdeeca45ca793c"
 | 
			
		||||
      sha256: "2d399f823b8849663744d2a9ddcce01c49268fb4170d0442a655bf6a2f47be22"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "3.0.6"
 | 
			
		||||
    version: "3.1.0"
 | 
			
		||||
  freezed_annotation:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: freezed_annotation
 | 
			
		||||
      sha256: c87ff004c8aa6af2d531668b46a4ea379f7191dc6dfa066acd53d506da6e044b
 | 
			
		||||
      sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "3.0.0"
 | 
			
		||||
    version: "3.1.0"
 | 
			
		||||
  frontend_server_client:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -1385,10 +1385,10 @@ packages:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: local_auth_darwin
 | 
			
		||||
      sha256: "630996cd7b7f28f5ab92432c4b35d055dd03a747bc319e5ffbb3c4806a3e50d2"
 | 
			
		||||
      sha256: "25163ce60a5a6c468cf7a0e3dc8a165f824cabc2aa9e39a5e9fc5c2311b7686f"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.4.3"
 | 
			
		||||
    version: "1.5.0"
 | 
			
		||||
  local_auth_platform_interface:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -2174,10 +2174,10 @@ packages:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: source_helper
 | 
			
		||||
      sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
 | 
			
		||||
      sha256: "4f81479fe5194a622cdd1713fe1ecb683a6e6c85cd8cec8e2e35ee5ab3fdf2a1"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.3.5"
 | 
			
		||||
    version: "1.3.6"
 | 
			
		||||
  source_map_stack_trace:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
 
 | 
			
		||||
@@ -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+110
 | 
			
		||||
version: 3.1.0+113
 | 
			
		||||
 | 
			
		||||
environment:
 | 
			
		||||
  sdk: ^3.7.2
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								web/.well-known/apple-app-site-association
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								web/.well-known/apple-app-site-association
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
{
 | 
			
		||||
  "applinks": {
 | 
			
		||||
    "apps": [],
 | 
			
		||||
    "details": [
 | 
			
		||||
      {
 | 
			
		||||
        "appIDs": [
 | 
			
		||||
          "W7HPZ53V6B.dev.solsynth.solian"
 | 
			
		||||
        ],
 | 
			
		||||
        "paths": [
 | 
			
		||||
          "*"
 | 
			
		||||
        ],
 | 
			
		||||
        "components": [
 | 
			
		||||
          {
 | 
			
		||||
            "/": "/*"
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  "webcredentials": {
 | 
			
		||||
    "apps": [
 | 
			
		||||
      "W7HPZ53V6B.dev.solsynth.solian"
 | 
			
		||||
    ]
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								web/.well-known/assetlinks.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								web/.well-known/assetlinks.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
[
 | 
			
		||||
  {
 | 
			
		||||
    "relation": ["delegate_permission/common.handle_all_urls"],
 | 
			
		||||
    "target": {
 | 
			
		||||
      "namespace": "android_app",
 | 
			
		||||
      "package_name": "dev.solsynth.solian",
 | 
			
		||||
      "sha256_cert_fingerprints": [
 | 
			
		||||
        "57:0C:A4:E6:1F:57:DF:56:70:42:05:4B:43:E2:DD:9E:00:E6:77:C3:D8:3C:5F:D5:A0:05:59:30:5A:85:F9:BC"
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
		Reference in New Issue
	
	Block a user