├── .github └── workflows │ └── build.yml ├── .gitignore ├── .metadata ├── .vscode └── settings.json ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle.kts │ ├── google-services.json │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── solian │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ └── splash.png │ │ │ ├── drawable-mdpi │ │ │ └── splash.png │ │ │ ├── drawable-night-hdpi │ │ │ └── splash.png │ │ │ ├── drawable-night-mdpi │ │ │ └── splash.png │ │ │ ├── drawable-night-v21 │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-night-xhdpi │ │ │ └── splash.png │ │ │ ├── drawable-night-xxhdpi │ │ │ └── splash.png │ │ │ ├── drawable-night-xxxhdpi │ │ │ └── splash.png │ │ │ ├── drawable-night │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-v21 │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-xhdpi │ │ │ └── splash.png │ │ │ ├── drawable-xxhdpi │ │ │ └── splash.png │ │ │ ├── drawable-xxxhdpi │ │ │ └── splash.png │ │ │ ├── drawable │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── values-night-v31 │ │ │ └── styles.xml │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ ├── values-v31 │ │ │ └── styles.xml │ │ │ ├── values │ │ │ └── styles.xml │ │ │ └── xml │ │ │ └── provider_paths.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle.kts ├── build │ └── reports │ │ └── problems │ │ └── problems-report.html ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle.kts ├── assets ├── fonts │ ├── Nunito-Bold.ttf │ ├── Nunito-Italic.ttf │ └── Nunito-Regular.ttf ├── i18n │ ├── en-US.json │ ├── zh-CN.json │ └── zh-TW.json └── icons │ ├── icon-dark.png │ ├── icon-padded.png │ └── icon.png ├── build.yaml ├── devtools_options.yaml ├── firebase.json ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-38x38@2x.png │ │ │ ├── Icon-App-38x38@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-64x64@2x.png │ │ │ ├── Icon-App-64x64@3x.png │ │ │ ├── Icon-App-68x68@2x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ ├── Icon-App-Dark-1024x1024@1x.png │ │ │ ├── Icon-App-Dark-20x20@2x.png │ │ │ ├── Icon-App-Dark-20x20@3x.png │ │ │ ├── Icon-App-Dark-29x29@2x.png │ │ │ ├── Icon-App-Dark-29x29@3x.png │ │ │ ├── Icon-App-Dark-38x38@2x.png │ │ │ ├── Icon-App-Dark-38x38@3x.png │ │ │ ├── Icon-App-Dark-40x40@2x.png │ │ │ ├── Icon-App-Dark-40x40@3x.png │ │ │ ├── Icon-App-Dark-60x60@2x.png │ │ │ ├── Icon-App-Dark-60x60@3x.png │ │ │ ├── Icon-App-Dark-64x64@2x.png │ │ │ ├── Icon-App-Dark-64x64@3x.png │ │ │ ├── Icon-App-Dark-68x68@2x.png │ │ │ ├── Icon-App-Dark-76x76@2x.png │ │ │ └── Icon-App-Dark-83.5x83.5@2x.png │ │ ├── LaunchBackground.imageset │ │ │ ├── Contents.json │ │ │ ├── background.png │ │ │ └── darkbackground.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── LaunchImageDark.png │ │ │ ├── LaunchImageDark@2x.png │ │ │ ├── LaunchImageDark@3x.png │ │ │ └── README.md │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── GoogleService-Info.plist │ ├── Info.plist │ ├── NotifyDelegate.swift │ ├── Runner-Bridging-Header.h │ ├── Runner.entitlements │ └── Services │ │ ├── CloudFile.swift │ │ └── DataExchange.swift ├── RunnerTests │ └── RunnerTests.swift └── SolianNotificationService │ ├── Info.plist │ └── NotificationService.swift ├── lib ├── database │ ├── database.native.dart │ ├── database.web.dart │ ├── drift_db.dart │ ├── drift_db.g.dart │ ├── message.dart │ └── message_repository.dart ├── firebase_options.dart ├── main.dart ├── models │ ├── activity.dart │ ├── activity.freezed.dart │ ├── activity.g.dart │ ├── auth.dart │ ├── auth.freezed.dart │ ├── auth.g.dart │ ├── badge.dart │ ├── chat.dart │ ├── chat.freezed.dart │ ├── chat.g.dart │ ├── file.dart │ ├── file.freezed.dart │ ├── file.g.dart │ ├── post.dart │ ├── post.freezed.dart │ ├── post.g.dart │ ├── realm.dart │ ├── realm.freezed.dart │ ├── realm.g.dart │ ├── relationship.dart │ ├── relationship.freezed.dart │ ├── relationship.g.dart │ ├── sticker.dart │ ├── sticker.freezed.dart │ ├── sticker.g.dart │ ├── user.dart │ ├── user.freezed.dart │ ├── user.g.dart │ ├── wallet.dart │ ├── wallet.freezed.dart │ └── wallet.g.dart ├── pods │ ├── call.dart │ ├── call.freezed.dart │ ├── call.g.dart │ ├── chat_summary.dart │ ├── chat_summary.g.dart │ ├── config.dart │ ├── config.freezed.dart │ ├── config.g.dart │ ├── database.dart │ ├── message.dart │ ├── network.dart │ ├── theme.dart │ ├── userinfo.dart │ ├── websocket.dart │ ├── websocket.freezed.dart │ └── websocket.g.dart ├── route.dart ├── route.gr.dart ├── screens │ ├── account.dart │ ├── account │ │ ├── me │ │ │ ├── event_calendar.dart │ │ │ ├── event_calendar.freezed.dart │ │ │ ├── event_calendar.g.dart │ │ │ ├── settings.dart │ │ │ └── update.dart │ │ ├── profile.dart │ │ ├── profile.g.dart │ │ ├── relationship.dart │ │ └── relationship.g.dart │ ├── auth │ │ ├── captcha.dart │ │ ├── captcha.native.dart │ │ ├── captcha.web.dart │ │ ├── create_account.dart │ │ ├── login.dart │ │ └── tabs.dart │ ├── chat │ │ ├── call.dart │ │ ├── chat.dart │ │ ├── chat.g.dart │ │ ├── room.dart │ │ ├── room.g.dart │ │ ├── room_detail.dart │ │ └── room_detail.freezed.dart │ ├── creators │ │ ├── hub.dart │ │ ├── hub.g.dart │ │ ├── publishers.dart │ │ ├── publishers.g.dart │ │ └── stickers │ │ │ ├── pack_detail.dart │ │ │ ├── pack_detail.freezed.dart │ │ │ ├── pack_detail.g.dart │ │ │ ├── stickers.dart │ │ │ └── stickers.g.dart │ ├── explore.dart │ ├── explore.g.dart │ ├── notification.dart │ ├── notification.g.dart │ ├── posts │ │ ├── compose.dart │ │ ├── detail.dart │ │ ├── detail.g.dart │ │ ├── pub_profile.dart │ │ └── pub_profile.g.dart │ ├── realm │ │ ├── detail.dart │ │ ├── detail.g.dart │ │ ├── realms.dart │ │ └── realms.g.dart │ ├── settings.dart │ ├── wallet.dart │ └── wallet.g.dart ├── services │ ├── file.dart │ ├── notify.dart │ ├── responsive.dart │ ├── tour.dart │ ├── tour.freezed.dart │ ├── tour.g.dart │ ├── udid.dart │ ├── udid.native.dart │ └── udid.web.dart └── widgets │ ├── account │ ├── account_picker.dart │ ├── account_picker.g.dart │ ├── badge.dart │ ├── leveling_progress.dart │ ├── status.dart │ ├── status.g.dart │ └── status_creation.dart │ ├── alert.dart │ ├── app_scaffold.dart │ ├── chat │ ├── call_button.dart │ ├── call_button.g.dart │ ├── call_overlay.dart │ ├── call_participant_tile.dart │ └── message_item.dart │ ├── check_in.dart │ ├── check_in.g.dart │ ├── content │ ├── alert.native.dart │ ├── alert.web.dart │ ├── attachment_preview.dart │ ├── cloud_file_collection.dart │ ├── cloud_file_picker.dart │ ├── cloud_files.dart │ ├── image.dart │ ├── markdown.dart │ ├── paging_helper_ext.dart │ ├── video.dart │ ├── video.native.dart │ └── video.web.dart │ ├── context_menu.dart │ ├── post │ ├── post_item.dart │ ├── post_list.dart │ ├── post_list.g.dart │ ├── post_quick_reply.dart │ ├── post_replies.dart │ ├── post_replies.g.dart │ └── publishers_modal.dart │ ├── realms │ └── selection_dropdown.dart │ ├── response.dart │ └── tour │ ├── techincal_review_intro.dart │ └── tour.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake └── runner │ ├── CMakeLists.txt │ ├── main.cc │ ├── my_application.cc │ └── my_application.h ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ └── app_icon_64.png │ ├── Base.lproj │ │ └── MainMenu.xib │ ├── Configs │ │ ├── AppInfo.xcconfig │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── GoogleService-Info.plist │ ├── Info.plist │ ├── MainFlutterWindow.swift │ └── Release.entitlements └── RunnerTests │ └── RunnerTests.swift ├── pubspec.lock ├── pubspec.yaml ├── web ├── _redirects ├── drift_worker.dart.js ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html ├── manifest.json ├── splash │ └── img │ │ ├── dark-1x.png │ │ ├── dark-2x.png │ │ ├── dark-3x.png │ │ ├── dark-4x.png │ │ ├── light-1x.png │ │ ├── light-2x.png │ │ ├── light-3x.png │ │ └── light-4x.png └── sqlite3.wasm └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build-web: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Clone repository 14 | uses: actions/checkout@v4 15 | - name: Set up Flutter 16 | uses: subosito/flutter-action@v2 17 | with: 18 | channel: stable 19 | cache: true 20 | - run: flutter pub get 21 | - run: flutter build web --release 22 | - name: Archive production artifacts 23 | uses: actions/upload-artifact@v4 24 | with: 25 | name: build-output-web 26 | path: build/web 27 | build-exe: 28 | runs-on: windows-latest 29 | steps: 30 | - name: Clone repository 31 | uses: actions/checkout@v4 32 | - name: Set up Flutter 33 | uses: subosito/flutter-action@v2 34 | with: 35 | channel: stable 36 | cache: true 37 | - run: flutter pub get 38 | - run: flutter build windows 39 | - name: Archive production artifacts 40 | uses: actions/upload-artifact@v4 41 | with: 42 | name: build-output-windows 43 | path: build/windows/x64/runner/Release 44 | build-linux: 45 | runs-on: ubuntu-latest 46 | steps: 47 | - name: Clone repository 48 | uses: actions/checkout@v4 49 | - name: Set up Flutter 50 | uses: subosito/flutter-action@v2 51 | with: 52 | channel: stable 53 | - run: | 54 | sudo apt-get update -y 55 | sudo apt-get install -y ninja-build libgtk-3-dev 56 | sudo apt-get install -y libmpv-dev mpv 57 | sudo apt-get install -y libayatana-appindicator3-dev 58 | sudo apt-get install -y keybinder-3.0 59 | sudo apt-get install -y libnotify-dev 60 | sudo apt-get install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev 61 | sudo apt-get install -y gstreamer-1.0 62 | - run: flutter pub get 63 | - run: flutter build linux 64 | - name: Archive production artifacts 65 | uses: actions/upload-artifact@v4 66 | with: 67 | name: build-output-linux 68 | path: build/linux/x64/release/bundle 69 | - name: Build AppImage 70 | run: | 71 | rm -r Solian.AppDir | true 72 | mkdir Solian.AppDir 73 | cp -r build/linux/x64/release/bundle/* Solian.AppDir 74 | cp -r buildtools/appimage_config/* Solian.AppDir 75 | cp assets/icon/icon-light-radius.png Solian.AppDir 76 | sudo chmod +x buildtools/appimagetool-x86_64.AppImage 77 | sudo chmod +x Solian.AppDir/AppRun 78 | ./buildtools/appimagetool-x86_64.AppImage Solian.AppDir 79 | - name: Archive production artifacts 80 | uses: actions/upload-artifact@v4 81 | with: 82 | name: build-output-linux-appimage 83 | path: './*.AppImage*' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .pub-cache/ 33 | .pub/ 34 | /build/ 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Android Studio will place build artifacts here 43 | /android/app/debug 44 | /android/app/profile 45 | /android/app/release 46 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "ea121f8859e4b13e47a8f845e4586164519588bc" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 17 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 18 | - platform: android 19 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 20 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 21 | - platform: ios 22 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 23 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 24 | - platform: linux 25 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 26 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 27 | - platform: macos 28 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 29 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 30 | - platform: web 31 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 32 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 33 | - platform: windows 34 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 35 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Solar Network 2 | 3 | > [!IMPORTANT] 4 | > You're visiting the Solar Network v3 branch, which is the next version and still under active development. **NOT READY FOR USE / PRODUCTION!** 5 | 6 | ![](https://solsynth.dev/_next/static/media/alpha.e779a584.webp) 7 | 8 | Hello there! Welcome to the main repository of the DysonNetwork (also known as the Solar Network). The code here is mainly about the front-end app (also known as Solian). But you can still post issues here to get help and request new features! 9 | 10 | ## Server 11 | 12 | The backend of the Solar Network project is located at [Solsynth/DysonNetwork](https://github.com/Solsynth/DysonNetwork) 13 | 14 | ## Tech Stack 15 | 16 | For those people who want to know the tech stack of this project, the front-end was built by Flutter, which provides cross-platform ability. 17 | The backend was built in .NET and PostgreSQL. 18 | 19 | If you want to contribute to the project, learn more about the [Code of Conduct](./CODE_OF_CONDUCT.md). 20 | 21 | ## Getting Started 22 | 23 | The content below will lead you to the world of Solar Network. 24 | 25 | ### For Normal Users 26 | 27 | **The v3 Release is not ready, yet.** 28 | 29 | 1. Go to the Github Releases page, and download the latest release / pre-release according to your platform. 30 | - **What's the difference between stable and pre-release?** The pre-release is untested by the other users and includes the new cutting-edge features, usually the pre-release is the feature drop. At the same time, due to we're not doing the API versioning, some breaking changes may break the stable release, so use the pre-release one instead. 31 | 2. Create an account on the Solar Network 32 | 3. Go to your email inbox to confirm your registration 33 | 4. Start exploring! 34 | 35 | ### For Developers 36 | 37 | To make the Solar Network App run in debug mode on your machine, you need to install the flutter development environment, for more environments, head to https://flutter.dev. 38 | 39 | For the Linux platform, you need to install those extra development libs: 40 | 41 | ```bash 42 | sudo apt-get update -y 43 | sudo apt-get install -y ninja-build libgtk-3-dev 44 | sudo apt-get install -y libmpv-dev mpv 45 | sudo apt-get install -y libayatana-appindicator3-dev 46 | sudo apt-get install -y keybinder-3.0 47 | sudo apt-get install -y libnotify-dev 48 | sudo apt-get install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev 49 | sudo apt-get install -y gstreamer-1.0 50 | ``` 51 | 52 | Then, use the flutter run for the app running in debug mode. 53 | 54 | ```bash 55 | flutter pub get 56 | ``` 57 | 58 | If you want to build the release version, use the flutter build command. Learn more from the flutter docs. 59 | 60 | ```bash 61 | flutter build 62 | ``` 63 | 64 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | analyzer: 28 | errors: 29 | invalid_annotation_target: ignore 30 | deprecated_member_use: ignore 31 | # Additional information about this file can be found at 32 | # https://dart.dev/guides/language/analysis-options 33 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | .cxx/ 9 | 10 | # Remember to never publicly share your keystore. 11 | # See https://flutter.dev/to/reference-keystore 12 | key.properties 13 | **/*.keystore 14 | **/*.jks 15 | -------------------------------------------------------------------------------- /android/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | // START: FlutterFire Configuration 4 | id("com.google.gms.google-services") 5 | // END: FlutterFire Configuration 6 | id("kotlin-android") 7 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 8 | id("dev.flutter.flutter-gradle-plugin") 9 | } 10 | 11 | android { 12 | namespace = "dev.solsynth.solian" 13 | compileSdk = flutter.compileSdkVersion 14 | ndkVersion = "29.0.13113456" 15 | 16 | compileOptions { 17 | sourceCompatibility = JavaVersion.VERSION_17 18 | targetCompatibility = JavaVersion.VERSION_17 19 | } 20 | 21 | kotlinOptions { 22 | jvmTarget = JavaVersion.VERSION_17.toString() 23 | } 24 | 25 | defaultConfig { 26 | applicationId = "dev.solsynth.solian" 27 | // You can update the following values to match your application needs. 28 | // For more information, see: https://flutter.dev/to/review-gradle-config. 29 | minSdk = 26 30 | targetSdk = flutter.targetSdkVersion 31 | versionCode = flutter.versionCode 32 | versionName = flutter.versionName 33 | } 34 | 35 | buildTypes { 36 | release { 37 | // TODO: Add your own signing config for the release build. 38 | // Signing with the debug keys for now, so `flutter run --release` works. 39 | signingConfig = signingConfigs.getByName("debug") 40 | } 41 | } 42 | } 43 | 44 | flutter { 45 | source = "../.." 46 | } 47 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "961776991058", 4 | "project_id": "solian-0x001", 5 | "storage_bucket": "solian-0x001.firebasestorage.app" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:961776991058:android:a8d3f7995b0b8e86f4188b", 11 | "android_client_info": { 12 | "package_name": "dev.solsynth.solian" 13 | } 14 | }, 15 | "oauth_client": [], 16 | "api_key": [ 17 | { 18 | "current_key": "AIzaSyDvFNudXYs29uDtcCv6pFR8h5tXBs90FYk" 19 | } 20 | ], 21 | "services": { 22 | "appinvite_service": { 23 | "other_platform_oauth_client": [] 24 | } 25 | } 26 | } 27 | ], 28 | "configuration_version": "1" 29 | } -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/solian/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package dev.solsynth.solian 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | import io.flutter.embedding.engine.FlutterEngine 5 | import io.flutter.plugins.sharedpreferences.LegacySharedPreferencesPlugin 6 | 7 | class MainActivity : FlutterActivity() 8 | { 9 | override fun configureFlutterEngine(flutterEngine: FlutterEngine) { 10 | super.configureFlutterEngine(flutterEngine) 11 | // https://github.com/flutter/flutter/issues/153075#issuecomment-2693189362 12 | flutterEngine.plugins.add(LegacySharedPreferencesPlugin()) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/drawable-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/drawable-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/drawable-night-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/drawable-night-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/drawable-night-v21/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/drawable-night-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/drawable-night-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/drawable-night-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/drawable-night/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/drawable-v21/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/drawable-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/drawable-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/drawable-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/drawable/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/mipmap-hdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/mipmap-mdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/provider_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() 9 | rootProject.layout.buildDirectory.value(newBuildDir) 10 | 11 | subprojects { 12 | val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) 13 | project.layout.buildDirectory.value(newSubprojectBuildDir) 14 | } 15 | subprojects { 16 | project.evaluationDependsOn(":app") 17 | } 18 | 19 | tasks.register("clean") { 20 | delete(rootProject.layout.buildDirectory) 21 | } 22 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | val flutterSdkPath = run { 3 | val properties = java.util.Properties() 4 | file("local.properties").inputStream().use { properties.load(it) } 5 | val flutterSdkPath = properties.getProperty("flutter.sdk") 6 | require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } 7 | flutterSdkPath 8 | } 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id("dev.flutter.flutter-plugin-loader") version "1.0.0" 21 | id("com.android.application") version "8.10.1" apply false 22 | // START: FlutterFire Configuration 23 | id("com.google.gms.google-services") version("4.3.15") apply false 24 | // END: FlutterFire Configuration 25 | id("org.jetbrains.kotlin.android") version "1.8.22" apply false 26 | } 27 | 28 | include(":app") 29 | -------------------------------------------------------------------------------- /assets/fonts/Nunito-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/assets/fonts/Nunito-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/Nunito-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/assets/fonts/Nunito-Italic.ttf -------------------------------------------------------------------------------- /assets/fonts/Nunito-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/assets/fonts/Nunito-Regular.ttf -------------------------------------------------------------------------------- /assets/icons/icon-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/assets/icons/icon-dark.png -------------------------------------------------------------------------------- /assets/icons/icon-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/assets/icons/icon-padded.png -------------------------------------------------------------------------------- /assets/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/assets/icons/icon.png -------------------------------------------------------------------------------- /build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | json_serializable: 5 | options: 6 | explicit_to_json: true 7 | field_rename: snake 8 | -------------------------------------------------------------------------------- /devtools_options.yaml: -------------------------------------------------------------------------------- 1 | description: This file stores settings for Dart & Flutter DevTools. 2 | documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states 3 | extensions: 4 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | {"flutter":{"platforms":{"android":{"default":{"projectId":"solian-0x001","appId":"1:961776991058:android:a8d3f7995b0b8e86f4188b","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"solian-0x001","appId":"1:961776991058:ios:727229d368cc47e1f4188b","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"macos":{"default":{"projectId":"solian-0x001","appId":"1:961776991058:ios:727229d368cc47e1f4188b","uploadDebugSymbols":false,"fileOutput":"macos/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"solian-0x001","configurations":{"android":"1:961776991058:android:a8d3f7995b0b8e86f4188b","ios":"1:961776991058:ios:727229d368cc47e1f4188b","macos":"1:961776991058:ios:727229d368cc47e1f4188b","web":"1:961776991058:web:b91d12f2892a5609f4188b","windows":"1:961776991058:web:3a912c0eb14028e5f4188b"}}}}}} -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '13.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | pod 'Alamofire' 35 | 36 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 37 | 38 | target 'RunnerTests' do 39 | inherit! :search_paths 40 | end 41 | 42 | target 'SolianNotificationService' do 43 | inherit! :search_paths 44 | pod 'Kingfisher', '~> 8.0' 45 | pod 'Alamofire' 46 | end 47 | end 48 | 49 | post_install do |installer| 50 | installer.pods_project.targets.each do |target| 51 | flutter_additional_ios_build_settings(target) 52 | target.build_configurations.each do |config| 53 | # Workaround for https://github.com/flutter/flutter/issues/64502 54 | config.build_settings['ONLY_ACTIVE_ARCH'] = 'YES' # <= this line 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | let notifyDelegate = NotifyDelegate() 7 | 8 | override func application( 9 | _ application: UIApplication, 10 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 11 | ) -> Bool { 12 | UNUserNotificationCenter.current().delegate = notifyDelegate 13 | 14 | GeneratedPluginRegistrant.register(with: self) 15 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-68x68@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-68x68@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-68x68@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-68x68@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "background.png", 5 | "idiom" : "universal" 6 | }, 7 | { 8 | "appearances" : [ 9 | { 10 | "appearance" : "luminosity", 11 | "value" : "dark" 12 | } 13 | ], 14 | "filename" : "darkbackground.png", 15 | "idiom" : "universal" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LaunchImage.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "LaunchImageDark.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "LaunchImage@2x.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "LaunchImageDark@2x.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "filename" : "LaunchImage@3x.png", 37 | "idiom" : "universal", 38 | "scale" : "3x" 39 | }, 40 | { 41 | "appearances" : [ 42 | { 43 | "appearance" : "luminosity", 44 | "value" : "dark" 45 | } 46 | ], 47 | "filename" : "LaunchImageDark@3x.png", 48 | "idiom" : "universal", 49 | "scale" : "3x" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | API_KEY 6 | AIzaSyCzQIyiYKoYHTpGXhN-IjgMML8z797WVD8 7 | GCM_SENDER_ID 8 | 961776991058 9 | PLIST_VERSION 10 | 1 11 | BUNDLE_ID 12 | dev.solsynth.solian 13 | PROJECT_ID 14 | solian-0x001 15 | STORAGE_BUCKET 16 | solian-0x001.firebasestorage.app 17 | IS_ADS_ENABLED 18 | 19 | IS_ANALYTICS_ENABLED 20 | 21 | IS_APPINVITE_ENABLED 22 | 23 | IS_GCM_ENABLED 24 | 25 | IS_SIGNIN_ENABLED 26 | 27 | GOOGLE_APP_ID 28 | 1:961776991058:ios:727229d368cc47e1f4188b 29 | 30 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ITSAppUsesNonExemptEncryption 6 | 7 | CADisableMinimumFrameDurationOnPhone 8 | 9 | CFBundleDevelopmentRegion 10 | $(DEVELOPMENT_LANGUAGE) 11 | CFBundleDisplayName 12 | Solian 13 | CFBundleExecutable 14 | $(EXECUTABLE_NAME) 15 | CFBundleIdentifier 16 | $(PRODUCT_BUNDLE_IDENTIFIER) 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundleName 20 | solian 21 | CFBundlePackageType 22 | APPL 23 | CFBundleShortVersionString 24 | $(FLUTTER_BUILD_NAME) 25 | CFBundleSignature 26 | ???? 27 | CFBundleVersion 28 | $(FLUTTER_BUILD_NUMBER) 29 | LSRequiresIPhoneOS 30 | 31 | NSCalendarsUsageDescription 32 | Grant access to Calander help us to shows Solar Calander with your own events. 33 | NSCameraUsageDescription 34 | Grant access to Camera will allow Solian take photo or video for your post. 35 | NSMicrophoneUsageDescription 36 | Grant access to Microphone will allow Solian record audio for your post. 37 | NSPhotoLibraryAddUsageDescription 38 | Grant access to Photo Library will allow Solian download photo to album for you. 39 | NSPhotoLibraryUsageDescription 40 | Grant access to Photo Library will allow Solian upload photo or video for your post. 41 | UIApplicationSupportsIndirectInputEvents 42 | 43 | UIBackgroundModes 44 | 45 | fetch 46 | audio 47 | remote-notification 48 | voip 49 | 50 | UILaunchStoryboardName 51 | LaunchScreen 52 | UIMainStoryboardFile 53 | Main 54 | UIStatusBarHidden 55 | 56 | UISupportedInterfaceOrientations 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationLandscapeLeft 60 | UIInterfaceOrientationLandscapeRight 61 | 62 | UISupportedInterfaceOrientations~ipad 63 | 64 | UIInterfaceOrientationPortrait 65 | UIInterfaceOrientationPortraitUpsideDown 66 | UIInterfaceOrientationLandscapeLeft 67 | UIInterfaceOrientationLandscapeRight 68 | 69 | NSUserActivityTypes 70 | 71 | INStartCallIntent 72 | INSendMessageIntent 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /ios/Runner/NotifyDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotifyDelegate.swift 3 | // Runner 4 | // 5 | // Created by LittleSheep on 2025/6/1. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | 11 | class NotifyDelegate: UIResponder, UNUserNotificationCenterDelegate { 12 | func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { 13 | if let textResponse = response as? UNTextInputNotificationResponse { 14 | let content = response.notification.request.content 15 | guard let metadata = content.userInfo["meta"] as? [AnyHashable: Any] else { 16 | return 17 | } 18 | 19 | var token: String? = UserDefaults.standard.getFlutterToken() 20 | if token == nil { 21 | return 22 | } 23 | 24 | let serverUrl = UserDefaults.standard.getServerUrl() 25 | let url = "\(serverUrl)/chat/\(metadata["room_id"] ?? "")/messages" 26 | 27 | let parameters: [String: Any?] = [ 28 | "content": textResponse.userText, 29 | "replied_message_id": metadata["message_id"] 30 | ] 31 | 32 | AF.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: HTTPHeaders( 33 | [HTTPHeader(name: "Authorization", value: "AtField \(token!)")] 34 | )) 35 | .validate() 36 | .responseString { response in 37 | switch response.result { 38 | case .success(_): 39 | break 40 | case .failure(let error): 41 | print("Failed to send chat reply message: \(error)") 42 | break 43 | } 44 | } 45 | } 46 | 47 | completionHandler() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /ios/Runner/Runner.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | com.apple.developer.usernotifications.communication 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner/Services/CloudFile.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CloudFile.swift 3 | // Runner 4 | // 5 | // Created by LittleSheep on 2025/5/31. 6 | // 7 | 8 | import Foundation 9 | 10 | func getAttachmentUrl(for identifier: String) -> String { 11 | let serverBaseUrl = "https://nt.solian.app" 12 | 13 | return identifier.starts(with: "http") ? identifier : "\(serverBaseUrl)/files/\(identifier)" 14 | } 15 | -------------------------------------------------------------------------------- /ios/Runner/Services/DataExchange.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataExchange.swift 3 | // Runner 4 | // 5 | // Created by LittleSheep on 2025/6/2. 6 | // 7 | 8 | import Foundation 9 | 10 | extension UserDefaults { 11 | func getFlutterValue(forKey key: String) -> T? { 12 | let prefixedKey = "flutter.\(key)" 13 | return self.object(forKey: prefixedKey) as? T 14 | } 15 | 16 | func getFlutterToken(forKey key: String = "dyn_user_tk") -> String? { 17 | let prefixedKey = "flutter.\(key)" 18 | guard let jsonString = self.string(forKey: prefixedKey), 19 | let data = jsonString.data(using: .utf8), 20 | let jsonObject = try? JSONSerialization.jsonObject(with: data), 21 | let jsonDict = jsonObject as? [String: Any], 22 | let token = jsonDict["token"] as? String else { 23 | return nil 24 | } 25 | return token 26 | } 27 | 28 | func getServerUrl(forKey key: String = "app_server_url") -> String { 29 | return self.getFlutterValue(forKey: key) ?? "https://nt.solian.app" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /ios/SolianNotificationService/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSUserActivityTypes 6 | 7 | INStartCallIntent 8 | INSendMessageIntent 9 | 10 | NSExtension 11 | 12 | NSExtensionPointIdentifier 13 | com.apple.usernotifications.service 14 | NSExtensionPrincipalClass 15 | $(PRODUCT_MODULE_NAME).NotificationService 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /lib/database/database.native.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:drift/drift.dart'; 4 | import 'package:drift/native.dart'; 5 | import 'package:island/database/drift_db.dart'; 6 | import 'package:path/path.dart' as p; 7 | import 'package:path_provider/path_provider.dart'; 8 | 9 | AppDatabase constructDb() { 10 | final db = LazyDatabase(() async { 11 | final dbFolder = await getApplicationDocumentsDirectory(); 12 | final file = File(p.join(dbFolder.path, 'solar_network_data.sqlite')); 13 | return NativeDatabase(file); 14 | }); 15 | return AppDatabase(db); 16 | } 17 | -------------------------------------------------------------------------------- /lib/database/database.web.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:drift/wasm.dart'; 3 | import 'package:island/database/drift_db.dart'; 4 | 5 | AppDatabase constructDb() { 6 | return AppDatabase(connectOnWeb()); 7 | } 8 | 9 | DatabaseConnection connectOnWeb() { 10 | return DatabaseConnection.delayed( 11 | Future(() async { 12 | final result = await WasmDatabase.open( 13 | databaseName: 'solar_network_data', 14 | sqlite3Uri: Uri.parse('sqlite3.wasm'), 15 | driftWorkerUri: Uri.parse('drift_worker.dart.js'), 16 | ); 17 | return result.resolvedExecutor; 18 | }), 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /lib/database/drift_db.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'package:drift/drift.dart'; 3 | import 'package:island/database/message.dart'; 4 | 5 | part 'drift_db.g.dart'; 6 | 7 | // Define the database 8 | @DriftDatabase(tables: [ChatMessages]) 9 | class AppDatabase extends _$AppDatabase { 10 | AppDatabase(super.e); 11 | 12 | @override 13 | int get schemaVersion => 2; 14 | 15 | @override 16 | MigrationStrategy get migration => MigrationStrategy( 17 | onCreate: (Migrator m) async { 18 | await m.createAll(); 19 | }, 20 | onUpgrade: (Migrator m, int from, int to) async { 21 | if (from < 2) { 22 | // Add isRead column with default value false 23 | await m.addColumn(chatMessages, chatMessages.isRead); 24 | } 25 | }, 26 | ); 27 | 28 | // Methods for chat messages 29 | Future> getMessagesForRoom( 30 | String roomId, { 31 | int offset = 0, 32 | int limit = 20, 33 | }) { 34 | return (select(chatMessages) 35 | ..where((m) => m.roomId.equals(roomId)) 36 | ..orderBy([(m) => OrderingTerm.desc(m.createdAt)]) 37 | ..limit(limit, offset: offset)) 38 | .get(); 39 | } 40 | 41 | Future saveMessage(ChatMessagesCompanion message) { 42 | return into(chatMessages).insert(message, mode: InsertMode.insertOrReplace); 43 | } 44 | 45 | Future updateMessage(ChatMessagesCompanion message) { 46 | return (update(chatMessages) 47 | ..where((m) => m.id.equals(message.id.value))).write(message); 48 | } 49 | 50 | Future updateMessageStatus(String id, MessageStatus status) { 51 | return (update(chatMessages)..where( 52 | (m) => m.id.equals(id), 53 | )).write(ChatMessagesCompanion(status: Value(status))); 54 | } 55 | 56 | Future markMessageAsRead(String id) { 57 | return (update(chatMessages)..where( 58 | (m) => m.id.equals(id), 59 | )).write(ChatMessagesCompanion(isRead: const Value(true))); 60 | } 61 | 62 | Future deleteMessage(String id) { 63 | return (delete(chatMessages)..where((m) => m.id.equals(id))).go(); 64 | } 65 | 66 | // Convert between Drift and model objects 67 | ChatMessagesCompanion messageToCompanion(LocalChatMessage message) { 68 | return ChatMessagesCompanion( 69 | id: Value(message.id), 70 | roomId: Value(message.roomId), 71 | senderId: Value(message.senderId), 72 | content: Value(message.toRemoteMessage().content), 73 | nonce: Value(message.nonce), 74 | data: Value(jsonEncode(message.data)), 75 | createdAt: Value(message.createdAt), 76 | status: Value(message.status), 77 | isRead: Value(message.isRead), 78 | ); 79 | } 80 | 81 | LocalChatMessage companionToMessage(ChatMessage dbMessage) { 82 | final data = jsonDecode(dbMessage.data); 83 | return LocalChatMessage( 84 | id: dbMessage.id, 85 | roomId: dbMessage.roomId, 86 | senderId: dbMessage.senderId, 87 | data: data, 88 | createdAt: dbMessage.createdAt, 89 | status: dbMessage.status, 90 | nonce: dbMessage.nonce, 91 | isRead: dbMessage.isRead, 92 | ); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /lib/database/message.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:island/models/chat.dart'; 3 | import 'package:island/models/file.dart'; 4 | 5 | class ChatMessages extends Table { 6 | TextColumn get id => text()(); 7 | TextColumn get roomId => text()(); 8 | TextColumn get senderId => text()(); 9 | TextColumn get content => text().nullable()(); 10 | TextColumn get nonce => text().nullable()(); 11 | TextColumn get data => text()(); 12 | DateTimeColumn get createdAt => dateTime()(); 13 | IntColumn get status => intEnum()(); 14 | BoolColumn get isRead => boolean().withDefault(const Constant(false))(); 15 | 16 | @override 17 | Set get primaryKey => {id}; 18 | } 19 | 20 | class LocalChatMessage { 21 | final String id; 22 | final String roomId; 23 | final String senderId; 24 | final Map data; 25 | final DateTime createdAt; 26 | MessageStatus status; 27 | final String? nonce; 28 | List? localAttachments; 29 | bool isRead; 30 | 31 | LocalChatMessage({ 32 | required this.id, 33 | required this.roomId, 34 | required this.senderId, 35 | required this.data, 36 | required this.createdAt, 37 | required this.nonce, 38 | required this.status, 39 | this.localAttachments, 40 | this.isRead = false, 41 | }); 42 | 43 | SnChatMessage toRemoteMessage() { 44 | return SnChatMessage.fromJson(data); 45 | } 46 | 47 | static LocalChatMessage fromRemoteMessage( 48 | SnChatMessage message, 49 | MessageStatus status, { 50 | String? nonce, 51 | bool isRead = false, 52 | }) { 53 | return LocalChatMessage( 54 | id: message.id, 55 | roomId: message.chatRoomId, 56 | senderId: message.senderId, 57 | data: message.toJson(), 58 | createdAt: message.createdAt, 59 | status: status, 60 | nonce: nonce ?? message.nonce, 61 | isRead: isRead, 62 | ); 63 | } 64 | } 65 | 66 | enum MessageStatus { pending, sent, failed } 67 | -------------------------------------------------------------------------------- /lib/firebase_options.dart: -------------------------------------------------------------------------------- 1 | // File generated by FlutterFire CLI. 2 | // ignore_for_file: type=lint 3 | import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; 4 | import 'package:flutter/foundation.dart' 5 | show defaultTargetPlatform, kIsWeb, TargetPlatform; 6 | 7 | /// Default [FirebaseOptions] for use with your Firebase apps. 8 | /// 9 | /// Example: 10 | /// ```dart 11 | /// import 'firebase_options.dart'; 12 | /// // ... 13 | /// await Firebase.initializeApp( 14 | /// options: DefaultFirebaseOptions.currentPlatform, 15 | /// ); 16 | /// ``` 17 | class DefaultFirebaseOptions { 18 | static FirebaseOptions get currentPlatform { 19 | if (kIsWeb) { 20 | return web; 21 | } 22 | switch (defaultTargetPlatform) { 23 | case TargetPlatform.android: 24 | return android; 25 | case TargetPlatform.iOS: 26 | return ios; 27 | case TargetPlatform.macOS: 28 | return macos; 29 | case TargetPlatform.windows: 30 | return windows; 31 | case TargetPlatform.linux: 32 | throw UnsupportedError( 33 | 'DefaultFirebaseOptions have not been configured for linux - ' 34 | 'you can reconfigure this by running the FlutterFire CLI again.', 35 | ); 36 | default: 37 | throw UnsupportedError( 38 | 'DefaultFirebaseOptions are not supported for this platform.', 39 | ); 40 | } 41 | } 42 | 43 | static const FirebaseOptions web = FirebaseOptions( 44 | apiKey: 'AIzaSyBKfIQpTouj5rXnlzkEieSlbAzepm4mgJE', 45 | appId: '1:961776991058:web:b91d12f2892a5609f4188b', 46 | messagingSenderId: '961776991058', 47 | projectId: 'solian-0x001', 48 | authDomain: 'solian-0x001.firebaseapp.com', 49 | storageBucket: 'solian-0x001.firebasestorage.app', 50 | measurementId: 'G-XY3HHKG0PE', 51 | ); 52 | 53 | static const FirebaseOptions android = FirebaseOptions( 54 | apiKey: 'AIzaSyDvFNudXYs29uDtcCv6pFR8h5tXBs90FYk', 55 | appId: '1:961776991058:android:a8d3f7995b0b8e86f4188b', 56 | messagingSenderId: '961776991058', 57 | projectId: 'solian-0x001', 58 | storageBucket: 'solian-0x001.firebasestorage.app', 59 | ); 60 | 61 | static const FirebaseOptions ios = FirebaseOptions( 62 | apiKey: 'AIzaSyCzQIyiYKoYHTpGXhN-IjgMML8z797WVD8', 63 | appId: '1:961776991058:ios:727229d368cc47e1f4188b', 64 | messagingSenderId: '961776991058', 65 | projectId: 'solian-0x001', 66 | storageBucket: 'solian-0x001.firebasestorage.app', 67 | iosBundleId: 'dev.solsynth.solian', 68 | ); 69 | 70 | static const FirebaseOptions macos = FirebaseOptions( 71 | apiKey: 'AIzaSyCzQIyiYKoYHTpGXhN-IjgMML8z797WVD8', 72 | appId: '1:961776991058:ios:727229d368cc47e1f4188b', 73 | messagingSenderId: '961776991058', 74 | projectId: 'solian-0x001', 75 | storageBucket: 'solian-0x001.firebasestorage.app', 76 | iosBundleId: 'dev.solsynth.solian', 77 | ); 78 | 79 | static const FirebaseOptions windows = FirebaseOptions( 80 | apiKey: 'AIzaSyCfgOdlcr7h8x8j0WKx_S2wXnGkOopq320', 81 | appId: '1:961776991058:web:3a912c0eb14028e5f4188b', 82 | messagingSenderId: '961776991058', 83 | projectId: 'solian-0x001', 84 | authDomain: 'solian-0x001.firebaseapp.com', 85 | storageBucket: 'solian-0x001.firebasestorage.app', 86 | measurementId: 'G-JD1YEG9D6F', 87 | ); 88 | 89 | } -------------------------------------------------------------------------------- /lib/models/activity.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:island/models/user.dart'; 3 | 4 | part 'activity.freezed.dart'; 5 | part 'activity.g.dart'; 6 | 7 | @freezed 8 | sealed class SnActivity with _$SnActivity { 9 | const factory SnActivity({ 10 | required String id, 11 | required String type, 12 | required String resourceIdentifier, 13 | required int visibility, 14 | required String accountId, 15 | required SnAccount account, 16 | required dynamic data, 17 | required DateTime createdAt, 18 | required DateTime updatedAt, 19 | required dynamic deletedAt, 20 | }) = _SnActivity; 21 | 22 | factory SnActivity.fromJson(Map json) => 23 | _$SnActivityFromJson(json); 24 | } 25 | 26 | @freezed 27 | sealed class SnCheckInResult with _$SnCheckInResult { 28 | const factory SnCheckInResult({ 29 | required String id, 30 | required int level, 31 | required List tips, 32 | required String accountId, 33 | required SnAccount? account, 34 | required DateTime createdAt, 35 | required DateTime updatedAt, 36 | required DateTime? deletedAt, 37 | }) = _SnCheckInResult; 38 | 39 | factory SnCheckInResult.fromJson(Map json) => 40 | _$SnCheckInResultFromJson(json); 41 | } 42 | 43 | @freezed 44 | sealed class SnFortuneTip with _$SnFortuneTip { 45 | const factory SnFortuneTip({ 46 | required bool isPositive, 47 | required String title, 48 | required String content, 49 | }) = _SnFortuneTip; 50 | 51 | factory SnFortuneTip.fromJson(Map json) => 52 | _$SnFortuneTipFromJson(json); 53 | } 54 | 55 | @freezed 56 | sealed class SnEventCalendarEntry with _$SnEventCalendarEntry { 57 | const factory SnEventCalendarEntry({ 58 | required DateTime date, 59 | required SnCheckInResult? checkInResult, 60 | required List statuses, 61 | }) = _SnEventCalendarEntry; 62 | 63 | factory SnEventCalendarEntry.fromJson(Map json) => 64 | _$SnEventCalendarEntryFromJson(json); 65 | } 66 | -------------------------------------------------------------------------------- /lib/models/auth.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'auth.freezed.dart'; 4 | part 'auth.g.dart'; 5 | 6 | @freezed 7 | sealed class AppToken with _$AppToken { 8 | const factory AppToken({required String token}) = _AppToken; 9 | 10 | factory AppToken.fromJson(Map json) => 11 | _$AppTokenFromJson(json); 12 | } 13 | 14 | @freezed 15 | sealed class SnAuthChallenge with _$SnAuthChallenge { 16 | const factory SnAuthChallenge({ 17 | required String id, 18 | required DateTime expiredAt, 19 | required int stepRemain, 20 | required int stepTotal, 21 | required List blacklistFactors, 22 | required List audiences, 23 | required List scopes, 24 | required String ipAddress, 25 | required String userAgent, 26 | required String? deviceId, 27 | required String? nonce, 28 | required DateTime createdAt, 29 | required DateTime updatedAt, 30 | required DateTime? deletedAt, 31 | }) = _SnAuthChallenge; 32 | 33 | factory SnAuthChallenge.fromJson(Map json) => 34 | _$SnAuthChallengeFromJson(json); 35 | } 36 | 37 | @freezed 38 | sealed class SnAuthFactor with _$SnAuthFactor { 39 | const factory SnAuthFactor({ 40 | required String id, 41 | required int type, 42 | required DateTime createdAt, 43 | required DateTime updatedAt, 44 | required DateTime? deletedAt, 45 | }) = _SnAuthFactor; 46 | 47 | factory SnAuthFactor.fromJson(Map json) => 48 | _$SnAuthFactorFromJson(json); 49 | } 50 | -------------------------------------------------------------------------------- /lib/models/auth.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'auth.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _AppToken _$AppTokenFromJson(Map json) => 10 | _AppToken(token: json['token'] as String); 11 | 12 | Map _$AppTokenToJson(_AppToken instance) => { 13 | 'token': instance.token, 14 | }; 15 | 16 | _SnAuthChallenge _$SnAuthChallengeFromJson(Map json) => 17 | _SnAuthChallenge( 18 | id: json['id'] as String, 19 | expiredAt: DateTime.parse(json['expired_at'] as String), 20 | stepRemain: (json['step_remain'] as num).toInt(), 21 | stepTotal: (json['step_total'] as num).toInt(), 22 | blacklistFactors: 23 | (json['blacklist_factors'] as List) 24 | .map((e) => e as String) 25 | .toList(), 26 | audiences: 27 | (json['audiences'] as List).map((e) => e as String).toList(), 28 | scopes: 29 | (json['scopes'] as List).map((e) => e as String).toList(), 30 | ipAddress: json['ip_address'] as String, 31 | userAgent: json['user_agent'] as String, 32 | deviceId: json['device_id'] as String?, 33 | nonce: json['nonce'] as String?, 34 | createdAt: DateTime.parse(json['created_at'] as String), 35 | updatedAt: DateTime.parse(json['updated_at'] as String), 36 | deletedAt: 37 | json['deleted_at'] == null 38 | ? null 39 | : DateTime.parse(json['deleted_at'] as String), 40 | ); 41 | 42 | Map _$SnAuthChallengeToJson(_SnAuthChallenge instance) => 43 | { 44 | 'id': instance.id, 45 | 'expired_at': instance.expiredAt.toIso8601String(), 46 | 'step_remain': instance.stepRemain, 47 | 'step_total': instance.stepTotal, 48 | 'blacklist_factors': instance.blacklistFactors, 49 | 'audiences': instance.audiences, 50 | 'scopes': instance.scopes, 51 | 'ip_address': instance.ipAddress, 52 | 'user_agent': instance.userAgent, 53 | 'device_id': instance.deviceId, 54 | 'nonce': instance.nonce, 55 | 'created_at': instance.createdAt.toIso8601String(), 56 | 'updated_at': instance.updatedAt.toIso8601String(), 57 | 'deleted_at': instance.deletedAt?.toIso8601String(), 58 | }; 59 | 60 | _SnAuthFactor _$SnAuthFactorFromJson(Map json) => 61 | _SnAuthFactor( 62 | id: json['id'] as String, 63 | type: (json['type'] as num).toInt(), 64 | createdAt: DateTime.parse(json['created_at'] as String), 65 | updatedAt: DateTime.parse(json['updated_at'] as String), 66 | deletedAt: 67 | json['deleted_at'] == null 68 | ? null 69 | : DateTime.parse(json['deleted_at'] as String), 70 | ); 71 | 72 | Map _$SnAuthFactorToJson(_SnAuthFactor instance) => 73 | { 74 | 'id': instance.id, 75 | 'type': instance.type, 76 | 'created_at': instance.createdAt.toIso8601String(), 77 | 'updated_at': instance.updatedAt.toIso8601String(), 78 | 'deleted_at': instance.deletedAt?.toIso8601String(), 79 | }; 80 | -------------------------------------------------------------------------------- /lib/models/badge.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class BadgeInfo { 4 | final String type; 5 | final String name; 6 | final String description; 7 | final IconData icon; 8 | final Color color; 9 | 10 | const BadgeInfo({ 11 | required this.type, 12 | required this.name, 13 | required this.description, 14 | this.icon = Icons.star, 15 | this.color = Colors.blue, 16 | }); 17 | } 18 | 19 | const Map kBadgeTemplates = { 20 | 'achievements.post.first': BadgeInfo( 21 | type: 'achievements.post.first', 22 | name: 'firstPostBadgeName', 23 | description: 'firstPostBadgeDescription', 24 | icon: Icons.create, 25 | color: Colors.green, 26 | ), 27 | 'achievements.post.popular': BadgeInfo( 28 | type: 'achievements.post.popular', 29 | name: 'popularPostBadgeName', 30 | description: 'popularPostBadgeDescription', 31 | icon: Icons.trending_up, 32 | color: Colors.orange, 33 | ), 34 | 'achievements.post.viral': BadgeInfo( 35 | type: 'achievements.post.viral', 36 | name: 'viralPostBadgeName', 37 | description: 'viralPostBadgeDescription', 38 | icon: Icons.whatshot, 39 | color: Colors.red, 40 | ), 41 | 'achievements.comment.helpful': BadgeInfo( 42 | type: 'achievements.comment.helpful', 43 | name: 'helpfulCommentBadgeName', 44 | description: 'helpfulCommentBadgeDescription', 45 | icon: Icons.thumb_up, 46 | color: Colors.lightBlue, 47 | ), 48 | 'ranks.newcomer': BadgeInfo( 49 | type: 'ranks.newcomer', 50 | name: 'newcomerBadgeName', 51 | description: 'newcomerBadgeDescription', 52 | icon: Icons.person_outline, 53 | color: Colors.blue, 54 | ), 55 | 'ranks.contributor': BadgeInfo( 56 | type: 'ranks.contributor', 57 | name: 'contributorBadgeName', 58 | description: 'contributorBadgeDescription', 59 | icon: Icons.stars, 60 | color: Colors.purple, 61 | ), 62 | 'ranks.expert': BadgeInfo( 63 | type: 'ranks.expert', 64 | name: 'expertBadgeName', 65 | description: 'expertBadgeDescription', 66 | icon: Icons.workspace_premium, 67 | color: Colors.amber, 68 | ), 69 | 'event.founder': BadgeInfo( 70 | type: 'event.founder', 71 | name: 'founderBadgeName', 72 | description: 'founderBadgeDescription', 73 | icon: Icons.foundation, 74 | color: Colors.deepPurple, 75 | ), 76 | 'event.beta.tester': BadgeInfo( 77 | type: 'event.beta.tester', 78 | name: 'betaTesterBadgeName', 79 | description: 'betaTesterBadgeDescription', 80 | icon: Icons.bug_report, 81 | color: Colors.teal, 82 | ), 83 | 'special.moderator': BadgeInfo( 84 | type: 'special.moderator', 85 | name: 'moderatorBadgeName', 86 | description: 'moderatorBadgeDescription', 87 | icon: Icons.construction, 88 | color: Colors.indigo, 89 | ), 90 | 'special.developer': BadgeInfo( 91 | type: 'special.developer', 92 | name: 'developerBadgeName', 93 | description: 'developerBadgeDescription', 94 | icon: Icons.code, 95 | color: Colors.indigo, 96 | ), 97 | 'special.translator': BadgeInfo( 98 | type: 'special.translator', 99 | name: 'translatorBadgeName', 100 | description: 'translatorBadgeDescription', 101 | icon: Icons.code, 102 | color: Colors.grey, 103 | ), 104 | }; 105 | -------------------------------------------------------------------------------- /lib/models/file.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'file.freezed.dart'; 4 | part 'file.g.dart'; 5 | 6 | enum UniversalFileType { image, video, audio, file } 7 | 8 | @freezed 9 | sealed class UniversalFile with _$UniversalFile { 10 | const UniversalFile._(); 11 | 12 | const factory UniversalFile({ 13 | required dynamic data, 14 | required UniversalFileType type, 15 | }) = _UniversalFile; 16 | 17 | bool get isOnCloud => data is SnCloudFile; 18 | bool get isOnDevice => !isOnCloud; 19 | 20 | factory UniversalFile.fromAttachment(SnCloudFile attachment) { 21 | return UniversalFile( 22 | data: attachment, 23 | type: switch (attachment.mimeType?.split('/').firstOrNull) { 24 | 'image' => UniversalFileType.image, 25 | 'audio' => UniversalFileType.audio, 26 | 'video' => UniversalFileType.video, 27 | _ => UniversalFileType.file, 28 | }, 29 | ); 30 | } 31 | } 32 | 33 | @freezed 34 | sealed class SnCloudFile with _$SnCloudFile { 35 | const factory SnCloudFile({ 36 | required String id, 37 | required String name, 38 | required String? description, 39 | required Map? fileMeta, 40 | required Map? userMeta, 41 | required String? mimeType, 42 | required String? hash, 43 | required int size, 44 | required DateTime? uploadedAt, 45 | required String? uploadedTo, 46 | required DateTime createdAt, 47 | required DateTime updatedAt, 48 | required DateTime? deletedAt, 49 | }) = _SnCloudFile; 50 | 51 | factory SnCloudFile.fromJson(Map json) => 52 | _$SnCloudFileFromJson(json); 53 | } 54 | -------------------------------------------------------------------------------- /lib/models/file.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'file.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _SnCloudFile _$SnCloudFileFromJson(Map json) => _SnCloudFile( 10 | id: json['id'] as String, 11 | name: json['name'] as String, 12 | description: json['description'] as String?, 13 | fileMeta: json['file_meta'] as Map?, 14 | userMeta: json['user_meta'] as Map?, 15 | mimeType: json['mime_type'] as String?, 16 | hash: json['hash'] as String?, 17 | size: (json['size'] as num).toInt(), 18 | uploadedAt: 19 | json['uploaded_at'] == null 20 | ? null 21 | : DateTime.parse(json['uploaded_at'] as String), 22 | uploadedTo: json['uploaded_to'] as String?, 23 | createdAt: DateTime.parse(json['created_at'] as String), 24 | updatedAt: DateTime.parse(json['updated_at'] as String), 25 | deletedAt: 26 | json['deleted_at'] == null 27 | ? null 28 | : DateTime.parse(json['deleted_at'] as String), 29 | ); 30 | 31 | Map _$SnCloudFileToJson(_SnCloudFile instance) => 32 | { 33 | 'id': instance.id, 34 | 'name': instance.name, 35 | 'description': instance.description, 36 | 'file_meta': instance.fileMeta, 37 | 'user_meta': instance.userMeta, 38 | 'mime_type': instance.mimeType, 39 | 'hash': instance.hash, 40 | 'size': instance.size, 41 | 'uploaded_at': instance.uploadedAt?.toIso8601String(), 42 | 'uploaded_to': instance.uploadedTo, 43 | 'created_at': instance.createdAt.toIso8601String(), 44 | 'updated_at': instance.updatedAt.toIso8601String(), 45 | 'deleted_at': instance.deletedAt?.toIso8601String(), 46 | }; 47 | -------------------------------------------------------------------------------- /lib/models/realm.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:island/models/file.dart'; 3 | import 'package:island/models/user.dart'; 4 | 5 | part 'realm.freezed.dart'; 6 | part 'realm.g.dart'; 7 | 8 | @freezed 9 | sealed class SnRealm with _$SnRealm { 10 | const factory SnRealm({ 11 | required String id, 12 | required String slug, 13 | required String name, 14 | required String description, 15 | required String? verifiedAs, 16 | required DateTime? verifiedAt, 17 | required bool isCommunity, 18 | required bool isPublic, 19 | required SnCloudFile? picture, 20 | required SnCloudFile? background, 21 | required String accountId, 22 | required DateTime createdAt, 23 | required DateTime updatedAt, 24 | required DateTime? deletedAt, 25 | }) = _SnRealm; 26 | 27 | factory SnRealm.fromJson(Map json) => 28 | _$SnRealmFromJson(json); 29 | } 30 | 31 | @freezed 32 | sealed class SnRealmMember with _$SnRealmMember { 33 | const factory SnRealmMember({ 34 | required String realmId, 35 | required SnRealm? realm, 36 | required String accountId, 37 | required SnAccount? account, 38 | required int role, 39 | required DateTime? joinedAt, 40 | required DateTime createdAt, 41 | required DateTime updatedAt, 42 | required DateTime? deletedAt, 43 | }) = _SnRealmMember; 44 | 45 | factory SnRealmMember.fromJson(Map json) => 46 | _$SnRealmMemberFromJson(json); 47 | } 48 | -------------------------------------------------------------------------------- /lib/models/relationship.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import 'package:island/models/user.dart'; 4 | 5 | part 'relationship.freezed.dart'; 6 | part 'relationship.g.dart'; 7 | 8 | @freezed 9 | sealed class SnRelationship with _$SnRelationship { 10 | const factory SnRelationship({ 11 | required DateTime? createdAt, 12 | required DateTime? updatedAt, 13 | required DateTime? deletedAt, 14 | required String accountId, 15 | required SnAccount account, 16 | required String relatedId, 17 | required SnAccount related, 18 | required DateTime? expiredAt, 19 | required int status, 20 | }) = _SnRelationship; 21 | 22 | factory SnRelationship.fromJson(Map json) => 23 | _$SnRelationshipFromJson(json); 24 | } 25 | -------------------------------------------------------------------------------- /lib/models/relationship.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'relationship.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _SnRelationship _$SnRelationshipFromJson(Map json) => 10 | _SnRelationship( 11 | createdAt: 12 | json['created_at'] == null 13 | ? null 14 | : DateTime.parse(json['created_at'] as String), 15 | updatedAt: 16 | json['updated_at'] == null 17 | ? null 18 | : DateTime.parse(json['updated_at'] as String), 19 | deletedAt: 20 | json['deleted_at'] == null 21 | ? null 22 | : DateTime.parse(json['deleted_at'] as String), 23 | accountId: json['account_id'] as String, 24 | account: SnAccount.fromJson(json['account'] as Map), 25 | relatedId: json['related_id'] as String, 26 | related: SnAccount.fromJson(json['related'] as Map), 27 | expiredAt: 28 | json['expired_at'] == null 29 | ? null 30 | : DateTime.parse(json['expired_at'] as String), 31 | status: (json['status'] as num).toInt(), 32 | ); 33 | 34 | Map _$SnRelationshipToJson(_SnRelationship instance) => 35 | { 36 | 'created_at': instance.createdAt?.toIso8601String(), 37 | 'updated_at': instance.updatedAt?.toIso8601String(), 38 | 'deleted_at': instance.deletedAt?.toIso8601String(), 39 | 'account_id': instance.accountId, 40 | 'account': instance.account.toJson(), 41 | 'related_id': instance.relatedId, 42 | 'related': instance.related.toJson(), 43 | 'expired_at': instance.expiredAt?.toIso8601String(), 44 | 'status': instance.status, 45 | }; 46 | -------------------------------------------------------------------------------- /lib/models/sticker.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:island/models/file.dart'; 3 | import 'package:island/models/post.dart'; 4 | 5 | part 'sticker.freezed.dart'; 6 | part 'sticker.g.dart'; 7 | 8 | @freezed 9 | sealed class SnSticker with _$SnSticker { 10 | const factory SnSticker({ 11 | required String id, 12 | required String slug, 13 | required String imageId, 14 | required SnCloudFile image, 15 | required String packId, 16 | required SnStickerPack? pack, 17 | required DateTime createdAt, 18 | required DateTime updatedAt, 19 | required DateTime? deletedAt, 20 | }) = _SnSticker; 21 | 22 | factory SnSticker.fromJson(Map json) => 23 | _$SnStickerFromJson(json); 24 | } 25 | 26 | @freezed 27 | sealed class SnStickerPack with _$SnStickerPack { 28 | const factory SnStickerPack({ 29 | required String id, 30 | required String name, 31 | required String description, 32 | required String prefix, 33 | required String publisherId, 34 | required SnPublisher? publisher, 35 | required DateTime createdAt, 36 | required DateTime updatedAt, 37 | required DateTime? deletedAt, 38 | }) = _SnStickerPack; 39 | 40 | factory SnStickerPack.fromJson(Map json) => 41 | _$SnStickerPackFromJson(json); 42 | } 43 | -------------------------------------------------------------------------------- /lib/models/sticker.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'sticker.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _SnSticker _$SnStickerFromJson(Map json) => _SnSticker( 10 | id: json['id'] as String, 11 | slug: json['slug'] as String, 12 | imageId: json['image_id'] as String, 13 | image: SnCloudFile.fromJson(json['image'] as Map), 14 | packId: json['pack_id'] as String, 15 | pack: 16 | json['pack'] == null 17 | ? null 18 | : SnStickerPack.fromJson(json['pack'] as Map), 19 | createdAt: DateTime.parse(json['created_at'] as String), 20 | updatedAt: DateTime.parse(json['updated_at'] as String), 21 | deletedAt: 22 | json['deleted_at'] == null 23 | ? null 24 | : DateTime.parse(json['deleted_at'] as String), 25 | ); 26 | 27 | Map _$SnStickerToJson(_SnSticker instance) => 28 | { 29 | 'id': instance.id, 30 | 'slug': instance.slug, 31 | 'image_id': instance.imageId, 32 | 'image': instance.image.toJson(), 33 | 'pack_id': instance.packId, 34 | 'pack': instance.pack?.toJson(), 35 | 'created_at': instance.createdAt.toIso8601String(), 36 | 'updated_at': instance.updatedAt.toIso8601String(), 37 | 'deleted_at': instance.deletedAt?.toIso8601String(), 38 | }; 39 | 40 | _SnStickerPack _$SnStickerPackFromJson(Map json) => 41 | _SnStickerPack( 42 | id: json['id'] as String, 43 | name: json['name'] as String, 44 | description: json['description'] as String, 45 | prefix: json['prefix'] as String, 46 | publisherId: json['publisher_id'] as String, 47 | publisher: 48 | json['publisher'] == null 49 | ? null 50 | : SnPublisher.fromJson(json['publisher'] as Map), 51 | createdAt: DateTime.parse(json['created_at'] as String), 52 | updatedAt: DateTime.parse(json['updated_at'] as String), 53 | deletedAt: 54 | json['deleted_at'] == null 55 | ? null 56 | : DateTime.parse(json['deleted_at'] as String), 57 | ); 58 | 59 | Map _$SnStickerPackToJson(_SnStickerPack instance) => 60 | { 61 | 'id': instance.id, 62 | 'name': instance.name, 63 | 'description': instance.description, 64 | 'prefix': instance.prefix, 65 | 'publisher_id': instance.publisherId, 66 | 'publisher': instance.publisher?.toJson(), 67 | 'created_at': instance.createdAt.toIso8601String(), 68 | 'updated_at': instance.updatedAt.toIso8601String(), 69 | 'deleted_at': instance.deletedAt?.toIso8601String(), 70 | }; 71 | -------------------------------------------------------------------------------- /lib/models/user.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:island/models/file.dart'; 3 | 4 | part 'user.freezed.dart'; 5 | part 'user.g.dart'; 6 | 7 | @freezed 8 | sealed class SnAccount with _$SnAccount { 9 | const factory SnAccount({ 10 | required String id, 11 | required String name, 12 | required String nick, 13 | required String language, 14 | required bool isSuperuser, 15 | required SnAccountProfile profile, 16 | @Default([]) List badges, 17 | required DateTime createdAt, 18 | required DateTime updatedAt, 19 | required DateTime? deletedAt, 20 | }) = _SnAccount; 21 | 22 | factory SnAccount.fromJson(Map json) => 23 | _$SnAccountFromJson(json); 24 | } 25 | 26 | @freezed 27 | sealed class SnAccountProfile with _$SnAccountProfile { 28 | const factory SnAccountProfile({ 29 | required String id, 30 | required String? firstName, 31 | required String? middleName, 32 | required String? lastName, 33 | @Default('') String bio, 34 | required int experience, 35 | required int level, 36 | required double levelingProgress, 37 | required SnCloudFile? picture, 38 | required SnCloudFile? background, 39 | required DateTime createdAt, 40 | required DateTime updatedAt, 41 | required DateTime? deletedAt, 42 | }) = _SnAccountProfile; 43 | 44 | factory SnAccountProfile.fromJson(Map json) => 45 | _$SnAccountProfileFromJson(json); 46 | } 47 | 48 | @freezed 49 | sealed class SnAccountStatus with _$SnAccountStatus { 50 | const factory SnAccountStatus({ 51 | required String id, 52 | required int attitude, 53 | required bool isOnline, 54 | required bool isInvisible, 55 | required bool isNotDisturb, 56 | required bool isCustomized, 57 | @Default("") String label, 58 | required DateTime? clearedAt, 59 | required String accountId, 60 | required DateTime createdAt, 61 | required DateTime updatedAt, 62 | required DateTime? deletedAt, 63 | }) = _SnAccountStatus; 64 | 65 | factory SnAccountStatus.fromJson(Map json) => 66 | _$SnAccountStatusFromJson(json); 67 | } 68 | 69 | @freezed 70 | sealed class SnAccountBadge with _$SnAccountBadge { 71 | const factory SnAccountBadge({ 72 | required String id, 73 | required String type, 74 | required String? label, 75 | required String? caption, 76 | required Map meta, 77 | required DateTime? expiredAt, 78 | required String accountId, 79 | required DateTime createdAt, 80 | required DateTime updatedAt, 81 | required DateTime? deletedAt, 82 | }) = _SnAccountBadge; 83 | 84 | factory SnAccountBadge.fromJson(Map json) => 85 | _$SnAccountBadgeFromJson(json); 86 | } 87 | 88 | @freezed 89 | sealed class SnNotification with _$SnNotification { 90 | const factory SnNotification({ 91 | required DateTime createdAt, 92 | required DateTime updatedAt, 93 | required DateTime? deletedAt, 94 | required String id, 95 | required String topic, 96 | required String title, 97 | @Default('') String subtitle, 98 | required String content, 99 | @Default({}) Map meta, 100 | required int priority, 101 | required DateTime? viewedAt, 102 | required String accountId, 103 | }) = _SnNotification; 104 | 105 | factory SnNotification.fromJson(Map json) => 106 | _$SnNotificationFromJson(json); 107 | } 108 | -------------------------------------------------------------------------------- /lib/models/wallet.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:island/models/user.dart'; 3 | 4 | part 'wallet.freezed.dart'; 5 | part 'wallet.g.dart'; 6 | 7 | @freezed 8 | sealed class SnWallet with _$SnWallet { 9 | const factory SnWallet({ 10 | required String id, 11 | required List pockets, 12 | required String accountId, 13 | required SnAccount? account, 14 | required DateTime createdAt, 15 | required DateTime updatedAt, 16 | required DateTime? deletedAt, 17 | }) = _SnWallet; 18 | 19 | factory SnWallet.fromJson(Map json) => 20 | _$SnWalletFromJson(json); 21 | } 22 | 23 | @freezed 24 | sealed class SnWalletPocket with _$SnWalletPocket { 25 | const factory SnWalletPocket({ 26 | required String id, 27 | required String currency, 28 | required double amount, 29 | required String walletId, 30 | required DateTime createdAt, 31 | required DateTime updatedAt, 32 | required DateTime? deletedAt, 33 | }) = _SnWalletPocket; 34 | 35 | factory SnWalletPocket.fromJson(Map json) => 36 | _$SnWalletPocketFromJson(json); 37 | } 38 | 39 | @freezed 40 | sealed class SnTransaction with _$SnTransaction { 41 | const factory SnTransaction({ 42 | required String id, 43 | required String currency, 44 | required double amount, 45 | required String? remarks, 46 | required int type, 47 | required String? payerWalletId, 48 | required SnWallet? payerWallet, 49 | required String? payeeWalletId, 50 | required SnWallet? payeeWallet, 51 | required DateTime createdAt, 52 | required DateTime updatedAt, 53 | required DateTime? deletedAt, 54 | }) = _SnTransaction; 55 | 56 | factory SnTransaction.fromJson(Map json) => 57 | _$SnTransactionFromJson(json); 58 | } 59 | -------------------------------------------------------------------------------- /lib/pods/call.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'call.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$callNotifierHash() => r'2082a572b5cfb4bf929dc1ed492c52cd2735452e'; 10 | 11 | /// See also [CallNotifier]. 12 | @ProviderFor(CallNotifier) 13 | final callNotifierProvider = 14 | AutoDisposeNotifierProvider.internal( 15 | CallNotifier.new, 16 | name: r'callNotifierProvider', 17 | debugGetCreateSourceHash: 18 | const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$callNotifierHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef _$CallNotifier = AutoDisposeNotifier; 26 | // ignore_for_file: type=lint 27 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 28 | -------------------------------------------------------------------------------- /lib/pods/chat_summary.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | import 'package:island/models/chat.dart'; 3 | import 'package:island/pods/network.dart'; 4 | 5 | part 'chat_summary.g.dart'; 6 | 7 | @riverpod 8 | class ChatSummary extends _$ChatSummary { 9 | @override 10 | Future> build() async { 11 | final client = ref.watch(apiClientProvider); 12 | final resp = await client.get('/chat/summary'); 13 | 14 | final Map data = resp.data; 15 | return data.map( 16 | (key, value) => MapEntry(key, SnChatSummary.fromJson(value)), 17 | ); 18 | } 19 | 20 | Future clearUnreadCount(String chatId) async { 21 | state.whenData((summaries) { 22 | final summary = summaries[chatId]; 23 | if (summary != null) { 24 | state = AsyncData({ 25 | ...summaries, 26 | chatId: SnChatSummary( 27 | unreadCount: 0, 28 | lastMessage: summary.lastMessage, 29 | ), 30 | }); 31 | } 32 | }); 33 | } 34 | 35 | void updateLastMessage(String chatId, SnChatMessage message) { 36 | state.whenData((summaries) { 37 | final summary = summaries[chatId]; 38 | if (summary != null) { 39 | state = AsyncData({ 40 | ...summaries, 41 | chatId: SnChatSummary( 42 | unreadCount: summary.unreadCount + 1, 43 | lastMessage: message, 44 | ), 45 | }); 46 | } 47 | }); 48 | } 49 | 50 | void incrementUnreadCount(String chatId) { 51 | state.whenData((summaries) { 52 | final summary = summaries[chatId]; 53 | if (summary != null) { 54 | state = AsyncData({ 55 | ...summaries, 56 | chatId: SnChatSummary( 57 | unreadCount: summary.unreadCount + 1, 58 | lastMessage: summary.lastMessage, 59 | ), 60 | }); 61 | } 62 | }); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/pods/chat_summary.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'chat_summary.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$chatSummaryHash() => r'19aad48b5fabb33a76b742400d3b738ceb81c40c'; 10 | 11 | /// See also [ChatSummary]. 12 | @ProviderFor(ChatSummary) 13 | final chatSummaryProvider = AutoDisposeAsyncNotifierProvider< 14 | ChatSummary, 15 | Map 16 | >.internal( 17 | ChatSummary.new, 18 | name: r'chatSummaryProvider', 19 | debugGetCreateSourceHash: 20 | const bool.fromEnvironment('dart.vm.product') ? null : _$chatSummaryHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef _$ChatSummary = AutoDisposeAsyncNotifier>; 26 | // ignore_for_file: type=lint 27 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 28 | -------------------------------------------------------------------------------- /lib/pods/config.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'config.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$appSettingsNotifierHash() => 10 | r'4f727d448ee17a87b5698b8e36ef67521655406c'; 11 | 12 | /// See also [AppSettingsNotifier]. 13 | @ProviderFor(AppSettingsNotifier) 14 | final appSettingsNotifierProvider = 15 | AutoDisposeNotifierProvider.internal( 16 | AppSettingsNotifier.new, 17 | name: r'appSettingsNotifierProvider', 18 | debugGetCreateSourceHash: 19 | const bool.fromEnvironment('dart.vm.product') 20 | ? null 21 | : _$appSettingsNotifierHash, 22 | dependencies: null, 23 | allTransitiveDependencies: null, 24 | ); 25 | 26 | typedef _$AppSettingsNotifier = AutoDisposeNotifier; 27 | // ignore_for_file: type=lint 28 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 29 | -------------------------------------------------------------------------------- /lib/pods/database.dart: -------------------------------------------------------------------------------- 1 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 2 | import 'package:island/database/drift_db.dart'; 3 | 4 | import 'package:island/database/database.native.dart' 5 | if (dart.library.html) 'package:island/database/database.web.dart'; 6 | 7 | final databaseProvider = Provider((ref) { 8 | final db = constructDb(); 9 | ref.onDispose(() => db.close()); 10 | return db; 11 | }); 12 | -------------------------------------------------------------------------------- /lib/pods/message.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | import 'package:island/pods/database.dart'; 6 | import 'package:path/path.dart'; 7 | import 'package:path_provider/path_provider.dart'; 8 | 9 | Future resetDatabase(WidgetRef ref) async { 10 | if (kIsWeb) return; 11 | 12 | final db = ref.read(databaseProvider); 13 | final basepath = await getApplicationSupportDirectory(); 14 | final file = File(join(basepath.path, 'solar_network_data.sqlite')); 15 | 16 | // Close current database connection 17 | db.close(); 18 | 19 | // Delete database file 20 | if (await file.exists()) { 21 | await file.delete(); 22 | } 23 | 24 | // Force refresh the database provider 25 | ref.invalidate(databaseProvider); 26 | } 27 | -------------------------------------------------------------------------------- /lib/pods/userinfo.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | import 'package:island/models/user.dart'; 6 | import 'package:island/pods/config.dart'; 7 | import 'package:island/pods/network.dart'; 8 | 9 | class UserInfoNotifier extends StateNotifier> { 10 | final Ref _ref; 11 | 12 | UserInfoNotifier(this._ref) : super(const AsyncValue.data(null)); 13 | 14 | Future getAccessToken() async { 15 | final prefs = _ref.read(sharedPreferencesProvider); 16 | return prefs.getString(kTokenPairStoreKey); 17 | } 18 | 19 | Future fetchUser() async { 20 | try { 21 | final client = _ref.read(apiClientProvider); 22 | final response = await client.get('/accounts/me'); 23 | final user = SnAccount.fromJson(response.data); 24 | state = AsyncValue.data(user); 25 | } catch (error, stackTrace) { 26 | log("[UserInfo] Failed to fetch user info: $error"); 27 | state = AsyncValue.error(error, stackTrace); 28 | } 29 | } 30 | 31 | Future logOut() async { 32 | state = const AsyncValue.data(null); 33 | final prefs = _ref.read(sharedPreferencesProvider); 34 | await prefs.remove(kTokenPairStoreKey); 35 | _ref.invalidate(userInfoProvider); 36 | } 37 | } 38 | 39 | final userInfoProvider = 40 | StateNotifierProvider>( 41 | (ref) => UserInfoNotifier(ref), 42 | ); 43 | -------------------------------------------------------------------------------- /lib/pods/websocket.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'websocket.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _WebSocketPacket _$WebSocketPacketFromJson(Map json) => 10 | _WebSocketPacket( 11 | type: json['type'] as String, 12 | data: json['data'] as Map?, 13 | errorMessage: json['error_message'] as String?, 14 | ); 15 | 16 | Map _$WebSocketPacketToJson(_WebSocketPacket instance) => 17 | { 18 | 'type': instance.type, 19 | 'data': instance.data, 20 | 'error_message': instance.errorMessage, 21 | }; 22 | -------------------------------------------------------------------------------- /lib/route.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_route/auto_route.dart'; 2 | import 'package:island/route.gr.dart'; 3 | 4 | @AutoRouterConfig(replaceInRouteName: 'Screen|Page,Route') 5 | class AppRouter extends RootStackRouter { 6 | @override 7 | RouteType get defaultRouteType => RouteType.adaptive(); 8 | 9 | @override 10 | List get routes => [ 11 | AutoRoute( 12 | page: ExploreShellRoute.page, 13 | path: '/', 14 | children: [ 15 | AutoRoute(page: ExploreRoute.page, path: ''), 16 | AutoRoute(page: PostComposeRoute.page, path: 'posts/compose'), 17 | AutoRoute(page: PostDetailRoute.page, path: 'posts/:id'), 18 | AutoRoute(page: PostEditRoute.page, path: 'posts/:id/edit'), 19 | AutoRoute(page: PublisherProfileRoute.page, path: 'publishers/:name'), 20 | ], 21 | ), 22 | AutoRoute( 23 | page: AccountShellRoute.page, 24 | path: '/account', 25 | children: [ 26 | AutoRoute(page: AccountRoute.page, path: ''), 27 | AutoRoute(page: NotificationRoute.page, path: 'notifications'), 28 | AutoRoute(page: WalletRoute.page, path: 'wallet'), 29 | AutoRoute(page: RelationshipRoute.page, path: 'relationships'), 30 | AutoRoute(page: AccountProfileRoute.page, path: ':name'), 31 | AutoRoute(page: UpdateProfileRoute.page, path: 'me/update'), 32 | AutoRoute(page: AccountSettingsRoute.page, path: 'settings'), 33 | ], 34 | ), 35 | AutoRoute(page: EventCalanderRoute.page, path: '/account/:name/calendar'), 36 | AutoRoute(page: RealmListRoute.page, path: '/realms'), 37 | AutoRoute( 38 | page: ChatShellRoute.page, 39 | path: '/chat', 40 | children: [ 41 | AutoRoute(page: ChatListRoute.page, path: ''), 42 | AutoRoute(page: ChatRoomRoute.page, path: ':id'), 43 | AutoRoute(page: CallRoute.page, path: ':id/call'), 44 | AutoRoute(page: NewChatRoute.page, path: 'new'), 45 | AutoRoute(page: EditChatRoute.page, path: ':id/edit'), 46 | AutoRoute(page: ChatDetailRoute.page, path: ':id/detail'), 47 | ], 48 | ), 49 | AutoRoute( 50 | page: CreatorHubShellRoute.page, 51 | path: '/creators', 52 | children: [ 53 | AutoRoute(page: CreatorHubRoute.page, path: ''), 54 | AutoRoute(page: StickersRoute.page, path: ':name/stickers'), 55 | AutoRoute(page: NewStickerPacksRoute.page, path: ':name/stickers/new'), 56 | AutoRoute( 57 | page: EditStickerPacksRoute.page, 58 | path: ':name/stickers/:packId/edit', 59 | ), 60 | AutoRoute( 61 | page: StickerPackDetailRoute.page, 62 | path: ':name/stickers/:packId', 63 | ), 64 | AutoRoute(page: NewStickersRoute.page, path: ':name/stickers/new'), 65 | AutoRoute( 66 | page: EditStickersRoute.page, 67 | path: ':name/stickers/:id/edit', 68 | ), 69 | AutoRoute(page: NewPublisherRoute.page, path: 'new'), 70 | AutoRoute(page: EditPublisherRoute.page, path: ':name/edit'), 71 | ], 72 | ), 73 | AutoRoute(page: LoginRoute.page, path: '/auth/login'), 74 | AutoRoute(page: CreateAccountRoute.page, path: '/auth/create-account'), 75 | AutoRoute(page: SettingsRoute.page, path: '/settings'), 76 | AutoRoute(page: NewRealmRoute.page, path: '/realms/new'), 77 | AutoRoute(page: RealmDetailRoute.page, path: '/realms/:slug'), 78 | AutoRoute(page: EditRealmRoute.page, path: '/realms/:slug/edit'), 79 | ]; 80 | } 81 | -------------------------------------------------------------------------------- /lib/screens/account/relationship.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'relationship.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$sentFriendRequestHash() => r'2efa72835b1740e0fe96bd347bce0f98b6ae80d6'; 10 | 11 | /// See also [sentFriendRequest]. 12 | @ProviderFor(sentFriendRequest) 13 | final sentFriendRequestProvider = 14 | AutoDisposeFutureProvider>.internal( 15 | sentFriendRequest, 16 | name: r'sentFriendRequestProvider', 17 | debugGetCreateSourceHash: 18 | const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$sentFriendRequestHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | @Deprecated('Will be removed in 3.0. Use Ref instead') 26 | // ignore: unused_element 27 | typedef SentFriendRequestRef = 28 | AutoDisposeFutureProviderRef>; 29 | String _$relationshipListNotifierHash() => 30 | r'560410cba6e4c26affd91aa86b3666319bd31f24'; 31 | 32 | /// See also [RelationshipListNotifier]. 33 | @ProviderFor(RelationshipListNotifier) 34 | final relationshipListNotifierProvider = AutoDisposeAsyncNotifierProvider< 35 | RelationshipListNotifier, 36 | CursorPagingData 37 | >.internal( 38 | RelationshipListNotifier.new, 39 | name: r'relationshipListNotifierProvider', 40 | debugGetCreateSourceHash: 41 | const bool.fromEnvironment('dart.vm.product') 42 | ? null 43 | : _$relationshipListNotifierHash, 44 | dependencies: null, 45 | allTransitiveDependencies: null, 46 | ); 47 | 48 | typedef _$RelationshipListNotifier = 49 | AutoDisposeAsyncNotifier>; 50 | // ignore_for_file: type=lint 51 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 52 | -------------------------------------------------------------------------------- /lib/screens/auth/captcha.dart: -------------------------------------------------------------------------------- 1 | export 'captcha.native.dart' if (dart.library.html) 'captcha.web.dart'; 2 | -------------------------------------------------------------------------------- /lib/screens/auth/captcha.native.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_inappwebview/flutter_inappwebview.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | import 'package:island/pods/config.dart'; 5 | import 'package:island/widgets/app_scaffold.dart'; 6 | 7 | class CaptchaScreen extends ConsumerWidget { 8 | const CaptchaScreen({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context, WidgetRef ref) { 12 | final serverUrl = ref.watch(serverUrlProvider); 13 | 14 | return AppScaffold( 15 | appBar: AppBar(title: Text("Anti-Robot")), 16 | body: InAppWebView( 17 | initialUrlRequest: URLRequest( 18 | url: WebUri('$serverUrl/auth/captcha?redirect_uri=solink://captcha'), 19 | ), 20 | shouldOverrideUrlLoading: (controller, navigationAction) async { 21 | Uri? url = navigationAction.request.url; 22 | if (url != null && url.queryParameters.containsKey('captcha_tk')) { 23 | Navigator.pop(context, url.queryParameters['captcha_tk']!); 24 | return NavigationActionPolicy.CANCEL; 25 | } 26 | return NavigationActionPolicy.ALLOW; 27 | }, 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/screens/auth/captcha.web.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: invalid_runtime_check_with_js_interop_types 2 | 3 | import 'dart:ui_web' as ui; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | import 'package:island/pods/config.dart'; 6 | import 'package:island/widgets/app_scaffold.dart'; 7 | import 'package:web/web.dart' as web; 8 | import 'package:flutter/material.dart'; 9 | 10 | class CaptchaScreen extends ConsumerStatefulWidget { 11 | const CaptchaScreen({super.key}); 12 | 13 | @override 14 | ConsumerState createState() => _CaptchaScreenState(); 15 | } 16 | 17 | class _CaptchaScreenState extends ConsumerState { 18 | bool _isInitialized = false; 19 | 20 | void _setupWebListener(String serverUrl) { 21 | web.window.onMessage.listen((event) { 22 | if (event.data != null && event.data is String) { 23 | final message = event.data as String; 24 | if (message.startsWith("captcha_tk=")) { 25 | String token = message.replaceFirst("captcha_tk=", ""); 26 | // ignore: use_build_context_synchronously 27 | if (context.mounted) Navigator.pop(context, token); 28 | } 29 | } 30 | }); 31 | 32 | final iframe = 33 | web.HTMLIFrameElement() 34 | ..src = '$serverUrl/auth/captcha' 35 | ..style.border = 'none' 36 | ..width = '100%' 37 | ..height = '100%'; 38 | 39 | web.document.body!.append(iframe); 40 | ui.platformViewRegistry.registerViewFactory( 41 | 'captcha-iframe', 42 | (int viewId) => iframe, 43 | ); 44 | 45 | setState(() { 46 | _isInitialized = true; 47 | }); 48 | } 49 | 50 | @override 51 | void initState() { 52 | super.initState(); 53 | Future.delayed(Duration.zero, () { 54 | final serverUrl = ref.watch(serverUrlProvider); 55 | _setupWebListener(serverUrl); 56 | }); 57 | } 58 | 59 | @override 60 | Widget build(BuildContext context) { 61 | return AppScaffold( 62 | appBar: AppBar(title: Text("Anti-Robot")), 63 | body: 64 | _isInitialized 65 | ? HtmlElementView(viewType: 'captcha-iframe') 66 | : Center(child: CircularProgressIndicator()), 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/screens/explore.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'explore.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$activityListNotifierHash() => 10 | r'8a67d302e828408c7c4cf724d84c2c5958f2dc7e'; 11 | 12 | /// See also [ActivityListNotifier]. 13 | @ProviderFor(ActivityListNotifier) 14 | final activityListNotifierProvider = AutoDisposeAsyncNotifierProvider< 15 | ActivityListNotifier, 16 | CursorPagingData 17 | >.internal( 18 | ActivityListNotifier.new, 19 | name: r'activityListNotifierProvider', 20 | debugGetCreateSourceHash: 21 | const bool.fromEnvironment('dart.vm.product') 22 | ? null 23 | : _$activityListNotifierHash, 24 | dependencies: null, 25 | allTransitiveDependencies: null, 26 | ); 27 | 28 | typedef _$ActivityListNotifier = 29 | AutoDisposeAsyncNotifier>; 30 | // ignore_for_file: type=lint 31 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 32 | -------------------------------------------------------------------------------- /lib/screens/notification.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'notification.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$notificationUnreadCountNotifierHash() => 10 | r'372a2cc259d7d838cd4f33a9129f7396ef31dbb9'; 11 | 12 | /// See also [NotificationUnreadCountNotifier]. 13 | @ProviderFor(NotificationUnreadCountNotifier) 14 | final notificationUnreadCountNotifierProvider = 15 | AutoDisposeAsyncNotifierProvider< 16 | NotificationUnreadCountNotifier, 17 | int 18 | >.internal( 19 | NotificationUnreadCountNotifier.new, 20 | name: r'notificationUnreadCountNotifierProvider', 21 | debugGetCreateSourceHash: 22 | const bool.fromEnvironment('dart.vm.product') 23 | ? null 24 | : _$notificationUnreadCountNotifierHash, 25 | dependencies: null, 26 | allTransitiveDependencies: null, 27 | ); 28 | 29 | typedef _$NotificationUnreadCountNotifier = AutoDisposeAsyncNotifier; 30 | String _$notificationListNotifierHash() => 31 | r'934a47bc2ce9e75699a4f53e2169470fd0c04a53'; 32 | 33 | /// See also [NotificationListNotifier]. 34 | @ProviderFor(NotificationListNotifier) 35 | final notificationListNotifierProvider = AutoDisposeAsyncNotifierProvider< 36 | NotificationListNotifier, 37 | CursorPagingData 38 | >.internal( 39 | NotificationListNotifier.new, 40 | name: r'notificationListNotifierProvider', 41 | debugGetCreateSourceHash: 42 | const bool.fromEnvironment('dart.vm.product') 43 | ? null 44 | : _$notificationListNotifierHash, 45 | dependencies: null, 46 | allTransitiveDependencies: null, 47 | ); 48 | 49 | typedef _$NotificationListNotifier = 50 | AutoDisposeAsyncNotifier>; 51 | // ignore_for_file: type=lint 52 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 53 | -------------------------------------------------------------------------------- /lib/screens/posts/detail.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_route/auto_route.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:gap/gap.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | import 'package:island/models/post.dart'; 6 | import 'package:island/pods/network.dart'; 7 | import 'package:island/services/responsive.dart'; 8 | import 'package:island/widgets/app_scaffold.dart'; 9 | import 'package:island/widgets/post/post_item.dart'; 10 | import 'package:island/widgets/post/post_quick_reply.dart'; 11 | import 'package:island/widgets/post/post_replies.dart'; 12 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 13 | import 'package:styled_widget/styled_widget.dart'; 14 | 15 | part 'detail.g.dart'; 16 | 17 | @riverpod 18 | Future post(Ref ref, String id) async { 19 | final client = ref.watch(apiClientProvider); 20 | final resp = await client.get('/posts/$id'); 21 | return SnPost.fromJson(resp.data); 22 | } 23 | 24 | @RoutePage() 25 | class PostDetailScreen extends HookConsumerWidget { 26 | final String id; 27 | const PostDetailScreen({super.key, @PathParam('id') required this.id}); 28 | 29 | @override 30 | Widget build(BuildContext context, WidgetRef ref) { 31 | final post = ref.watch(postProvider(id)); 32 | 33 | final isWide = isWideScreen(context); 34 | 35 | return AppScaffold( 36 | appBar: AppBar(title: const Text('Post')), 37 | body: post.when( 38 | data: (post) { 39 | return Stack( 40 | fit: StackFit.expand, 41 | children: [ 42 | CustomScrollView( 43 | slivers: [ 44 | SliverToBoxAdapter( 45 | child: Column( 46 | children: [ 47 | PostItem( 48 | item: post!, 49 | isOpenable: false, 50 | backgroundColor: isWide ? Colors.transparent : null, 51 | ), 52 | const Divider(height: 1), 53 | ], 54 | ), 55 | ), 56 | PostRepliesList(postId: id), 57 | SliverGap(MediaQuery.of(context).padding.bottom + 80), 58 | ], 59 | ), 60 | Positioned( 61 | bottom: 0, 62 | left: 0, 63 | right: 0, 64 | child: Material( 65 | elevation: 2, 66 | color: Colors.transparent, 67 | child: PostQuickReply( 68 | parent: post, 69 | onPosted: () { 70 | ref.invalidate(postRepliesNotifierProvider(id)); 71 | }, 72 | ).padding( 73 | bottom: MediaQuery.of(context).padding.bottom + 16, 74 | top: 16, 75 | horizontal: 16, 76 | ), 77 | ), 78 | ), 79 | ], 80 | ); 81 | }, 82 | loading: () => const Center(child: CircularProgressIndicator()), 83 | error: (e, _) => Text('Error: $e'), 84 | ), 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/screens/wallet.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'wallet.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$walletCurrentHash() => r'94e6f3776ce15679d17238e372660c365c9b1028'; 10 | 11 | /// See also [walletCurrent]. 12 | @ProviderFor(walletCurrent) 13 | final walletCurrentProvider = AutoDisposeFutureProvider.internal( 14 | walletCurrent, 15 | name: r'walletCurrentProvider', 16 | debugGetCreateSourceHash: 17 | const bool.fromEnvironment('dart.vm.product') 18 | ? null 19 | : _$walletCurrentHash, 20 | dependencies: null, 21 | allTransitiveDependencies: null, 22 | ); 23 | 24 | @Deprecated('Will be removed in 3.0. Use Ref instead') 25 | // ignore: unused_element 26 | typedef WalletCurrentRef = AutoDisposeFutureProviderRef; 27 | String _$transactionListNotifierHash() => 28 | r'148ffb0ee9e3be3b92de432f314d8ee2f09e9a24'; 29 | 30 | /// See also [TransactionListNotifier]. 31 | @ProviderFor(TransactionListNotifier) 32 | final transactionListNotifierProvider = AutoDisposeAsyncNotifierProvider< 33 | TransactionListNotifier, 34 | CursorPagingData 35 | >.internal( 36 | TransactionListNotifier.new, 37 | name: r'transactionListNotifierProvider', 38 | debugGetCreateSourceHash: 39 | const bool.fromEnvironment('dart.vm.product') 40 | ? null 41 | : _$transactionListNotifierHash, 42 | dependencies: null, 43 | allTransitiveDependencies: null, 44 | ); 45 | 46 | typedef _$TransactionListNotifier = 47 | AutoDisposeAsyncNotifier>; 48 | // ignore_for_file: type=lint 49 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 50 | -------------------------------------------------------------------------------- /lib/services/notify.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | import 'dart:io'; 3 | 4 | import 'package:dio/dio.dart'; 5 | import 'package:firebase_messaging/firebase_messaging.dart'; 6 | import 'package:flutter/foundation.dart'; 7 | 8 | Future subscribePushNotification(Dio apiClient) async { 9 | await FirebaseMessaging.instance.requestPermission( 10 | provisional: true, 11 | alert: true, 12 | badge: true, 13 | sound: true, 14 | ); 15 | 16 | String? deviceToken; 17 | if (kIsWeb) { 18 | deviceToken = await FirebaseMessaging.instance.getToken( 19 | vapidKey: 20 | "BFN2mkqyeI6oi4d2PAV4pfNyG3Jy0FBEblmmPrjmP0r5lHOPrxrcqLIWhM21R_cicF-j4Xhtr1kyDyDgJYRPLgU", 21 | ); 22 | } else if (Platform.isAndroid) { 23 | deviceToken = await FirebaseMessaging.instance.getToken(); 24 | } else if (Platform.isIOS) { 25 | deviceToken = await FirebaseMessaging.instance.getAPNSToken(); 26 | } 27 | 28 | FirebaseMessaging.instance.onTokenRefresh 29 | .listen((fcmToken) { 30 | _putTokenToRemote(apiClient, fcmToken, 1); 31 | }) 32 | .onError((err) { 33 | log("Failed to get firebase cloud messaging push token: $err"); 34 | }); 35 | 36 | if (deviceToken != null) { 37 | _putTokenToRemote( 38 | apiClient, 39 | deviceToken, 40 | !kIsWeb && (Platform.isIOS || Platform.isMacOS) ? 0 : 1, 41 | ); 42 | } 43 | } 44 | 45 | Future _putTokenToRemote( 46 | Dio apiClient, 47 | String token, 48 | int provider, 49 | ) async { 50 | await apiClient.put( 51 | "/notifications/subscription", 52 | data: {"provider": provider, "device_token": token}, 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /lib/services/responsive.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | const kWideScreenWidth = 768.0; 4 | const kWiderScreenWidth = 1024.0; 5 | const kWidescreenWidth = 1280.0; 6 | 7 | bool isWideScreen(BuildContext context) { 8 | return MediaQuery.of(context).size.width > kWideScreenWidth; 9 | } 10 | 11 | bool isWiderScreen(BuildContext context) { 12 | return MediaQuery.of(context).size.width > kWiderScreenWidth; 13 | } 14 | 15 | bool isWidestScreen(BuildContext context) { 16 | return MediaQuery.of(context).size.width > kWidescreenWidth; 17 | } 18 | -------------------------------------------------------------------------------- /lib/services/tour.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/widgets.dart'; 4 | import 'package:freezed_annotation/freezed_annotation.dart'; 5 | import 'package:island/pods/config.dart'; 6 | import 'package:island/widgets/tour/techincal_review_intro.dart'; 7 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 8 | 9 | part 'tour.g.dart'; 10 | part 'tour.freezed.dart'; 11 | 12 | const kAppTourStatusKey = "app_tour_statuses"; 13 | 14 | const List kAllTours = [ 15 | Tour(id: 'technical_review_intro', isStartup: true), 16 | ]; 17 | 18 | @freezed 19 | sealed class Tour with _$Tour { 20 | const Tour._(); 21 | 22 | const factory Tour({required String id, required bool isStartup}) = _Tour; 23 | 24 | Widget get widget => switch (id) { 25 | 'technical_review_intro' => const TechicalReviewIntroWidget(), 26 | _ => throw UnimplementedError(), 27 | }; 28 | } 29 | 30 | @riverpod 31 | class TourStatusNotifier extends _$TourStatusNotifier { 32 | @override 33 | Map build() { 34 | final prefs = ref.watch(sharedPreferencesProvider); 35 | final storedJson = prefs.getString(kAppTourStatusKey); 36 | if (storedJson != null) { 37 | try { 38 | final Map stored = jsonDecode(storedJson); 39 | return Map.from(stored); 40 | } catch (_) { 41 | return {for (final id in kAllTours.map((e) => e.id)) id: false}; 42 | } 43 | } 44 | return {for (final id in kAllTours.map((e) => e.id)) id: false}; 45 | } 46 | 47 | bool isTourShown(String tourId) => state[tourId] ?? false; 48 | 49 | Future _saveState(Map newState) async { 50 | state = newState; 51 | final prefs = ref.read(sharedPreferencesProvider); 52 | await prefs.setString(kAppTourStatusKey, jsonEncode(newState)); 53 | } 54 | 55 | Future showTour(String tourId) async { 56 | if (!isTourShown(tourId)) { 57 | final newState = {...state, tourId: true}; 58 | await _saveState(newState); 59 | return kAllTours.firstWhere((e) => e.id == tourId).widget; 60 | } 61 | return null; 62 | } 63 | 64 | Future resetTours() async { 65 | final newState = {for (final id in kAllTours.map((e) => e.id)) id: false}; 66 | await _saveState(newState); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/services/tour.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'tour.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$tourStatusNotifierHash() => 10 | r'ee712e1f8010311df8f24838814ab5c451f9e593'; 11 | 12 | /// See also [TourStatusNotifier]. 13 | @ProviderFor(TourStatusNotifier) 14 | final tourStatusNotifierProvider = 15 | AutoDisposeNotifierProvider>.internal( 16 | TourStatusNotifier.new, 17 | name: r'tourStatusNotifierProvider', 18 | debugGetCreateSourceHash: 19 | const bool.fromEnvironment('dart.vm.product') 20 | ? null 21 | : _$tourStatusNotifierHash, 22 | dependencies: null, 23 | allTransitiveDependencies: null, 24 | ); 25 | 26 | typedef _$TourStatusNotifier = AutoDisposeNotifier>; 27 | // ignore_for_file: type=lint 28 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 29 | -------------------------------------------------------------------------------- /lib/services/udid.dart: -------------------------------------------------------------------------------- 1 | export 'udid.native.dart' if (dart.library.html) 'udid.web.dart'; 2 | -------------------------------------------------------------------------------- /lib/services/udid.native.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_udid/flutter_udid.dart'; 2 | 3 | Future getUdid() async { 4 | return await FlutterUdid.consistentUdid; 5 | } 6 | -------------------------------------------------------------------------------- /lib/services/udid.web.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:crypto/crypto.dart'; 4 | import 'package:web/web.dart'; 5 | 6 | Future getUdid() async { 7 | final userAgent = window.navigator.userAgent; 8 | final bytes = utf8.encode(userAgent); 9 | final hash = sha256.convert(bytes); 10 | return hash.toString(); 11 | } 12 | -------------------------------------------------------------------------------- /lib/widgets/account/badge.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:island/models/user.dart'; 4 | import 'package:island/models/badge.dart'; 5 | 6 | class BadgeList extends StatelessWidget { 7 | final List badges; 8 | const BadgeList({super.key, required this.badges}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Wrap( 13 | spacing: 8, 14 | runSpacing: 8, 15 | children: badges.map((badge) => BadgeItem(badge: badge)).toList(), 16 | ); 17 | } 18 | } 19 | 20 | class BadgeItem extends StatelessWidget { 21 | final SnAccountBadge badge; 22 | const BadgeItem({super.key, required this.badge}); 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | final template = kBadgeTemplates[badge.type]; 27 | final name = badge.label ?? template?.name.tr() ?? 'unknown'.tr(); 28 | final description = badge.caption ?? template?.description.tr() ?? ''; 29 | 30 | return Tooltip( 31 | message: '$name\n$description', 32 | child: Container( 33 | padding: const EdgeInsets.all(4), 34 | decoration: BoxDecoration( 35 | color: (template?.color ?? Colors.blue).withOpacity(0.1), 36 | shape: BoxShape.circle, 37 | ), 38 | child: Icon( 39 | template?.icon ?? Icons.stars, 40 | color: template?.color ?? Colors.orange, 41 | size: 20, 42 | ), 43 | ), 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/widgets/account/leveling_progress.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gap/gap.dart'; 3 | import 'package:google_fonts/google_fonts.dart'; 4 | import 'package:easy_localization/easy_localization.dart'; 5 | import 'package:styled_widget/styled_widget.dart'; 6 | 7 | class LevelingProgressCard extends StatelessWidget { 8 | final int level; 9 | final int experience; 10 | final double progress; 11 | 12 | const LevelingProgressCard({ 13 | super.key, 14 | required this.level, 15 | required this.experience, 16 | required this.progress, 17 | }); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Card( 22 | child: Column( 23 | crossAxisAlignment: CrossAxisAlignment.stretch, 24 | children: [ 25 | Text('levelingProgress').tr().fontSize(16).bold(), 26 | Row( 27 | spacing: 8, 28 | crossAxisAlignment: CrossAxisAlignment.baseline, 29 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 30 | textBaseline: TextBaseline.alphabetic, 31 | children: [ 32 | Text( 33 | 'levelingProgressLevel'.tr(args: [level.toString()]), 34 | style: GoogleFonts.robotoMono(), 35 | ).fontSize(13).bold(), 36 | Text( 37 | 'levelingProgressExperience'.tr(args: [experience.toString()]), 38 | style: GoogleFonts.robotoMono(), 39 | ).fontSize(13), 40 | ], 41 | ), 42 | const Gap(8), 43 | Tooltip( 44 | message: '${(progress).toStringAsFixed(1)}%', 45 | child: LinearProgressIndicator( 46 | minHeight: 4, 47 | value: progress / 100, 48 | color: Theme.of(context).colorScheme.primary, 49 | backgroundColor: 50 | Theme.of(context).colorScheme.surfaceContainerHigh, 51 | ), 52 | ), 53 | ], 54 | ).padding(horizontal: 16, vertical: 12), 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/widgets/alert.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:gap/gap.dart'; 4 | import 'package:styled_widget/styled_widget.dart'; 5 | 6 | export 'content/alert.native.dart' 7 | if (dart.library.html) 'content/alert.web.dart'; 8 | 9 | void showSnackBar( 10 | BuildContext context, 11 | String message, { 12 | SnackBarAction? action, 13 | }) { 14 | ScaffoldMessenger.of( 15 | context, 16 | ).showSnackBar(SnackBar(content: Text(message), action: action)); 17 | } 18 | 19 | void clearSnackBar(BuildContext context) { 20 | ScaffoldMessenger.of(context).clearSnackBars(); 21 | } 22 | 23 | OverlayEntry? _loadingOverlay; 24 | GlobalKey<_FadeOverlayState> _loadingOverlayKey = GlobalKey(); 25 | 26 | class _FadeOverlay extends StatefulWidget { 27 | const _FadeOverlay({super.key, required this.child}); 28 | final Widget child; 29 | 30 | @override 31 | State<_FadeOverlay> createState() => _FadeOverlayState(); 32 | } 33 | 34 | class _FadeOverlayState extends State<_FadeOverlay> { 35 | bool _visible = false; 36 | 37 | @override 38 | void initState() { 39 | super.initState(); 40 | WidgetsBinding.instance.addPostFrameCallback((_) { 41 | setState(() => _visible = true); 42 | }); 43 | } 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | return AnimatedOpacity( 48 | opacity: _visible ? 1.0 : 0.0, 49 | duration: const Duration(milliseconds: 200), 50 | child: widget.child, 51 | ); 52 | } 53 | } 54 | 55 | void showLoadingModal(BuildContext context) { 56 | if (_loadingOverlay != null) return; 57 | 58 | _loadingOverlay = OverlayEntry( 59 | builder: 60 | (context) => _FadeOverlay( 61 | key: _loadingOverlayKey, 62 | child: Material( 63 | color: Colors.black54, 64 | child: Center( 65 | child: Material( 66 | color: Theme.of(context).colorScheme.surface, 67 | borderRadius: BorderRadius.circular(8), 68 | elevation: 4, 69 | child: Column( 70 | mainAxisSize: MainAxisSize.min, 71 | children: [ 72 | CircularProgressIndicator(year2023: true), 73 | const Gap(24), 74 | Text('loading'.tr()), 75 | ], 76 | ).padding(all: 32), 77 | ), 78 | ), 79 | ), 80 | ), 81 | ); 82 | 83 | Overlay.of(context).insert(_loadingOverlay!); 84 | } 85 | 86 | void hideLoadingModal(BuildContext context) async { 87 | if (_loadingOverlay == null) return; 88 | 89 | final entry = _loadingOverlay!; 90 | _loadingOverlay = null; 91 | 92 | final state = entry.mounted ? _loadingOverlayKey.currentState : null; 93 | 94 | if (state != null) { 95 | // ignore: invalid_use_of_protected_member 96 | state.setState(() => state._visible = false); 97 | await Future.delayed(const Duration(milliseconds: 200)); 98 | } 99 | 100 | entry.remove(); 101 | } 102 | -------------------------------------------------------------------------------- /lib/widgets/check_in.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'check_in.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$checkInResultTodayHash() => 10 | r'0e2af6c1f419b2ee74ee38b6fb5d8071498e75c8'; 11 | 12 | /// See also [checkInResultToday]. 13 | @ProviderFor(checkInResultToday) 14 | final checkInResultTodayProvider = 15 | AutoDisposeFutureProvider.internal( 16 | checkInResultToday, 17 | name: r'checkInResultTodayProvider', 18 | debugGetCreateSourceHash: 19 | const bool.fromEnvironment('dart.vm.product') 20 | ? null 21 | : _$checkInResultTodayHash, 22 | dependencies: null, 23 | allTransitiveDependencies: null, 24 | ); 25 | 26 | @Deprecated('Will be removed in 3.0. Use Ref instead') 27 | // ignore: unused_element 28 | typedef CheckInResultTodayRef = AutoDisposeFutureProviderRef; 29 | // ignore_for_file: type=lint 30 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 31 | -------------------------------------------------------------------------------- /lib/widgets/content/alert.native.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:easy_localization/easy_localization.dart'; 5 | import 'package:flutter_platform_alert/flutter_platform_alert.dart'; 6 | 7 | String _parseRemoteError(DioException err) { 8 | log('${err.requestOptions.method} ${err.requestOptions.uri} ${err.message}'); 9 | if (err.response?.data is String) return err.response?.data; 10 | if (err.response?.data?['errors'] != null) { 11 | final errors = err.response?.data['errors'] as Map; 12 | return errors.values 13 | .map( 14 | (ele) => 15 | (ele as List).map((ele) => ele.toString()).join('\n'), 16 | ) 17 | .join('\n'); 18 | } 19 | return err.message ?? err.toString(); 20 | } 21 | 22 | void showErrorAlert(dynamic err) async { 23 | final text = switch (err) { 24 | String _ => err, 25 | DioException _ => _parseRemoteError(err), 26 | Exception _ => err.toString(), 27 | _ => err.toString(), 28 | }; 29 | FlutterPlatformAlert.showAlert( 30 | windowTitle: 'somethingWentWrong'.tr(), 31 | text: text, 32 | alertStyle: AlertButtonStyle.ok, 33 | iconStyle: IconStyle.error, 34 | ); 35 | } 36 | 37 | void showInfoAlert(String message, String title) async { 38 | FlutterPlatformAlert.showAlert( 39 | windowTitle: title, 40 | text: message, 41 | alertStyle: AlertButtonStyle.ok, 42 | iconStyle: IconStyle.information, 43 | ); 44 | } 45 | 46 | Future showConfirmAlert(String message, String title) async { 47 | final result = await FlutterPlatformAlert.showAlert( 48 | windowTitle: title, 49 | text: message, 50 | alertStyle: AlertButtonStyle.okCancel, 51 | iconStyle: IconStyle.question, 52 | ); 53 | return result == AlertButton.okButton; 54 | } 55 | -------------------------------------------------------------------------------- /lib/widgets/content/alert.web.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_web_libraries_in_flutter 2 | 3 | import 'dart:developer'; 4 | import 'dart:js' as js; 5 | 6 | import 'package:dio/dio.dart'; 7 | import 'package:easy_localization/easy_localization.dart'; 8 | 9 | String _parseRemoteError(DioException err) { 10 | log('${err.requestOptions.method} ${err.requestOptions.uri} ${err.message}'); 11 | if (err.response?.data is String) return err.response?.data; 12 | if (err.response?.data?['errors'] != null) { 13 | final errors = err.response?.data['errors'] as Map; 14 | return errors.values 15 | .map( 16 | (ele) => 17 | (ele as List).map((ele) => ele.toString()).join('\n'), 18 | ) 19 | .join('\n'); 20 | } 21 | return err.message ?? err.toString(); 22 | } 23 | 24 | void showErrorAlert(dynamic err) async { 25 | final text = switch (err) { 26 | String _ => err, 27 | DioException _ => _parseRemoteError(err), 28 | Exception _ => err.toString(), 29 | _ => err.toString(), 30 | }; 31 | js.context.callMethod('swal', ['somethingWentWrong'.tr(), text, 'error']); 32 | } 33 | 34 | void showInfoAlert(String message, String title) async { 35 | js.context.callMethod('swal', [title, message, 'info']); 36 | } 37 | 38 | Future showConfirmAlert(String message, String title) async { 39 | final result = await js.context.callMethod('swal', [ 40 | title, 41 | message, 42 | 'question', 43 | {'buttons': true}, 44 | ]); 45 | return result == true; 46 | } 47 | -------------------------------------------------------------------------------- /lib/widgets/content/image.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_blurhash/flutter_blurhash.dart'; 4 | 5 | class UniversalImage extends StatelessWidget { 6 | final String uri; 7 | final String? blurHash; 8 | final BoxFit fit; 9 | final double? width; 10 | final double? height; 11 | final bool noCacheOptimization; 12 | 13 | const UniversalImage({ 14 | super.key, 15 | required this.uri, 16 | this.blurHash, 17 | this.fit = BoxFit.cover, 18 | this.width, 19 | this.height, 20 | this.noCacheOptimization = false, 21 | }); 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | int? cacheWidth; 26 | int? cacheHeight; 27 | if (width != null && height != null && !noCacheOptimization) { 28 | final devicePixelRatio = MediaQuery.of(context).devicePixelRatio; 29 | cacheWidth = width != null ? (width! * devicePixelRatio).round() : null; 30 | cacheHeight = 31 | height != null ? (height! * devicePixelRatio).round() : null; 32 | } 33 | 34 | return SizedBox( 35 | width: width, 36 | height: height, 37 | child: Stack( 38 | fit: StackFit.expand, 39 | children: [ 40 | if (blurHash != null) BlurHash(hash: blurHash!), 41 | CachedNetworkImage( 42 | imageUrl: uri, 43 | fit: fit, 44 | width: width, 45 | height: height, 46 | memCacheHeight: cacheHeight, 47 | memCacheWidth: cacheWidth, 48 | ), 49 | ], 50 | ), 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/widgets/content/video.dart: -------------------------------------------------------------------------------- 1 | export 'video.native.dart' if (dart.library.html) 'video.web.dart'; 2 | -------------------------------------------------------------------------------- /lib/widgets/content/video.native.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | import 'dart:io'; 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_cache_manager/flutter_cache_manager.dart'; 6 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 7 | import 'package:island/pods/network.dart'; 8 | import 'package:island/widgets/alert.dart'; 9 | import 'package:media_kit/media_kit.dart'; 10 | import 'package:media_kit_video/media_kit_video.dart'; 11 | 12 | class UniversalVideo extends ConsumerStatefulWidget { 13 | final String uri; 14 | final double aspectRatio; 15 | const UniversalVideo({ 16 | super.key, 17 | required this.uri, 18 | this.aspectRatio = 16 / 9, 19 | }); 20 | 21 | @override 22 | ConsumerState createState() => _UniversalVideoState(); 23 | } 24 | 25 | class _UniversalVideoState extends ConsumerState { 26 | Player? _player; 27 | VideoController? _videoController; 28 | 29 | void _openVideo() async { 30 | final url = widget.uri; 31 | MediaKit.ensureInitialized(); 32 | 33 | _player = Player(); 34 | _videoController = VideoController(_player!); 35 | 36 | String? uri; 37 | final inCacheInfo = await DefaultCacheManager().getFileFromCache(url); 38 | if (inCacheInfo == null) { 39 | log('[MediaPlayer] Miss cache: $url'); 40 | final token = await getToken(ref.watch(tokenProvider)); 41 | final fileStream = DefaultCacheManager().getFileStream( 42 | url, 43 | headers: {'Authorization': 'Bearer $token'}, 44 | withProgress: true, 45 | ); 46 | await for (var fileInfo in fileStream) { 47 | if (fileInfo is FileInfo) { 48 | uri = fileInfo.file.path; 49 | break; 50 | } 51 | } 52 | } else { 53 | uri = inCacheInfo.file.path; 54 | log('[MediaPlayer] Hit cache: $url'); 55 | } 56 | if (uri == null) { 57 | showErrorAlert('Failed to open media... $url'); 58 | return; 59 | } 60 | 61 | _player!.open(Media(uri), play: false); 62 | } 63 | 64 | @override 65 | void initState() { 66 | super.initState(); 67 | _openVideo(); 68 | } 69 | 70 | @override 71 | void dispose() { 72 | super.dispose(); 73 | _player?.dispose(); 74 | } 75 | 76 | @override 77 | Widget build(BuildContext context) { 78 | if (_videoController == null) { 79 | return Center(child: CircularProgressIndicator()); 80 | } 81 | 82 | return Video( 83 | controller: _videoController!, 84 | aspectRatio: widget.aspectRatio, 85 | controls: 86 | !kIsWeb && (Platform.isAndroid || Platform.isIOS) 87 | ? MaterialVideoControls 88 | : MaterialDesktopVideoControls, 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/widgets/content/video.web.dart: -------------------------------------------------------------------------------- 1 | import 'package:web/web.dart' as web; 2 | import 'package:flutter/material.dart'; 3 | 4 | class UniversalVideo extends StatelessWidget { 5 | final String uri; 6 | final double aspectRatio; 7 | const UniversalVideo({ 8 | super.key, 9 | required this.uri, 10 | required this.aspectRatio, 11 | }); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return HtmlElementView.fromTagName( 16 | tagName: 'video', 17 | onElementCreated: (element) { 18 | element as web.HTMLVideoElement; 19 | element.src = uri; 20 | element.style.width = '100%'; 21 | element.style.height = '100%'; 22 | element.controls = true; 23 | }, 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/widgets/context_menu.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_hooks/flutter_hooks.dart'; 4 | 5 | typedef ContextMenuBuilder = 6 | Widget Function(BuildContext context, Offset offset); 7 | 8 | class ContextMenuRegion extends HookWidget { 9 | final Offset? mobileAnchor; 10 | final Widget child; 11 | final ContextMenuBuilder contextMenuBuilder; 12 | const ContextMenuRegion({ 13 | super.key, 14 | required this.child, 15 | required this.contextMenuBuilder, 16 | this.mobileAnchor, 17 | }); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | final contextMenuController = useMemoized(() => ContextMenuController()); 22 | final mobileOffset = useState(null); 23 | 24 | bool canBeTouchScreen = switch (defaultTargetPlatform) { 25 | TargetPlatform.android || TargetPlatform.iOS => true, 26 | _ => false, 27 | }; 28 | 29 | void showMenu(Offset position) { 30 | contextMenuController.show( 31 | context: context, 32 | contextMenuBuilder: (BuildContext context) { 33 | return contextMenuBuilder(context, position); 34 | }, 35 | ); 36 | } 37 | 38 | void hideMenu() { 39 | contextMenuController.remove(); 40 | } 41 | 42 | void onSecondaryTapUp(TapUpDetails details) { 43 | showMenu(details.globalPosition); 44 | } 45 | 46 | void onTap() { 47 | if (!contextMenuController.isShown) { 48 | return; 49 | } 50 | hideMenu(); 51 | } 52 | 53 | void onLongPressStart(LongPressStartDetails details) { 54 | mobileOffset.value = details.globalPosition; 55 | } 56 | 57 | void onLongPress() { 58 | assert(mobileOffset.value != null); 59 | showMenu(mobileAnchor ?? mobileOffset.value!); 60 | mobileOffset.value = null; 61 | } 62 | 63 | useEffect(() { 64 | return () { 65 | hideMenu(); 66 | }; 67 | }, []); 68 | 69 | return TapRegion( 70 | behavior: HitTestBehavior.opaque, 71 | child: GestureDetector( 72 | behavior: HitTestBehavior.opaque, 73 | onSecondaryTapUp: onSecondaryTapUp, 74 | onTap: onTap, 75 | onLongPress: canBeTouchScreen ? onLongPress : null, 76 | onLongPressStart: canBeTouchScreen ? onLongPressStart : null, 77 | child: child, 78 | ), 79 | onTapOutside: (_) { 80 | hideMenu(); 81 | }, 82 | ); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/widgets/post/post_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 3 | import 'package:island/models/post.dart'; 4 | import 'package:island/pods/network.dart'; 5 | import 'package:island/widgets/content/paging_helper_ext.dart'; 6 | import 'package:island/widgets/post/post_item.dart'; 7 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 8 | import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; 9 | 10 | part 'post_list.g.dart'; 11 | 12 | @riverpod 13 | class PostListNotifier extends _$PostListNotifier 14 | with CursorPagingNotifierMixin { 15 | static const int _pageSize = 20; 16 | 17 | @override 18 | Future> build(String? pubName) { 19 | this.pubName = pubName; 20 | return fetch(cursor: null); 21 | } 22 | 23 | @override 24 | Future> fetch({required String? cursor}) async { 25 | final client = ref.read(apiClientProvider); 26 | final offset = cursor == null ? 0 : int.parse(cursor); 27 | 28 | final queryParams = { 29 | 'offset': offset, 30 | 'take': _pageSize, 31 | if (pubName != null) 'pub': pubName, 32 | }; 33 | 34 | final response = await client.get('/posts', queryParameters: queryParams); 35 | final total = int.parse(response.headers.value('X-Total') ?? '0'); 36 | final List data = response.data; 37 | final posts = data.map((json) => SnPost.fromJson(json)).toList(); 38 | 39 | final hasMore = offset + posts.length < total; 40 | final nextCursor = hasMore ? (offset + posts.length).toString() : null; 41 | 42 | return CursorPagingData( 43 | items: posts, 44 | hasMore: hasMore, 45 | nextCursor: nextCursor, 46 | ); 47 | } 48 | } 49 | 50 | class SliverPostList extends HookConsumerWidget { 51 | final String? pubName; 52 | const SliverPostList({super.key, this.pubName}); 53 | 54 | @override 55 | Widget build(BuildContext context, WidgetRef ref) { 56 | return PagingHelperSliverView( 57 | provider: postListNotifierProvider(pubName), 58 | futureRefreshable: postListNotifierProvider(pubName).future, 59 | notifierRefreshable: postListNotifierProvider(pubName).notifier, 60 | contentBuilder: 61 | (data, widgetCount, endItemView) => SliverList.builder( 62 | itemCount: widgetCount, 63 | itemBuilder: (context, index) { 64 | if (index == widgetCount - 1) { 65 | return endItemView; 66 | } 67 | 68 | return Column( 69 | children: [ 70 | PostItem(item: data.items[index]), 71 | const Divider(height: 1), 72 | ], 73 | ); 74 | }, 75 | ), 76 | ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lib/widgets/realms/selection_dropdown.dart: -------------------------------------------------------------------------------- 1 | import 'package:dropdown_button2/dropdown_button2.dart'; 2 | import 'package:easy_localization/easy_localization.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:island/models/realm.dart'; 5 | import 'package:island/widgets/content/cloud_files.dart'; 6 | import 'package:material_symbols_icons/symbols.dart'; 7 | 8 | class RealmSelectionDropdown extends StatelessWidget { 9 | final SnRealm? value; 10 | final List realms; 11 | final ValueChanged onChanged; 12 | final bool isLoading; 13 | final String? error; 14 | 15 | const RealmSelectionDropdown({ 16 | super.key, 17 | required this.value, 18 | required this.realms, 19 | required this.onChanged, 20 | this.isLoading = false, 21 | this.error, 22 | }); 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return DropdownButtonHideUnderline( 27 | child: DropdownButton2( 28 | isExpanded: true, 29 | hint: Text('realmSelection').tr(), 30 | value: value, 31 | items: [ 32 | DropdownMenuItem( 33 | value: null, 34 | child: Row( 35 | children: [ 36 | const CircleAvatar( 37 | radius: 16, 38 | child: Icon(Symbols.person, fill: 1), 39 | ), 40 | const SizedBox(width: 12), 41 | Text('individual').tr(), 42 | ], 43 | ), 44 | ), 45 | if (!isLoading && error == null) 46 | ...realms.map( 47 | (realm) => DropdownMenuItem( 48 | value: realm, 49 | child: Row( 50 | children: [ 51 | ProfilePictureWidget( 52 | fileId: realm.picture?.id, 53 | fallbackIcon: Symbols.workspaces, 54 | radius: 16, 55 | ), 56 | const SizedBox(width: 12), 57 | Text(realm.name), 58 | ], 59 | ), 60 | ), 61 | ), 62 | ], 63 | onChanged: onChanged, 64 | buttonStyleData: const ButtonStyleData( 65 | padding: EdgeInsets.only(left: 4, right: 16), 66 | ), 67 | ), 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/widgets/response.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:easy_localization/easy_localization.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:gap/gap.dart'; 5 | import 'package:material_symbols_icons/symbols.dart'; 6 | import 'package:styled_widget/styled_widget.dart'; 7 | 8 | class ResponseErrorWidget extends StatelessWidget { 9 | final dynamic error; 10 | final VoidCallback onRetry; 11 | 12 | const ResponseErrorWidget({super.key, required this.error, required this.onRetry}); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Column( 17 | mainAxisAlignment: MainAxisAlignment.center, 18 | children: [ 19 | const Icon(Symbols.error_outline, size: 48), 20 | const Gap(4), 21 | if (error is DioException && error.response?.statusCode == 401) 22 | ConstrainedBox( 23 | constraints: const BoxConstraints(maxWidth: 320), 24 | child: Column( 25 | children: [ 26 | Text( 27 | 'unauthorized'.tr(), 28 | textAlign: TextAlign.center, 29 | style: const TextStyle(color: Color(0xFF757575)), 30 | ).bold(), 31 | Text( 32 | 'unauthorizedHint'.tr(), 33 | textAlign: TextAlign.center, 34 | style: const TextStyle(color: Color(0xFF757575)), 35 | ), 36 | ], 37 | ), 38 | ).center() 39 | else 40 | ConstrainedBox( 41 | constraints: const BoxConstraints(maxWidth: 320), 42 | child: Text( 43 | error.toString(), 44 | textAlign: TextAlign.center, 45 | style: const TextStyle(color: Color(0xFF757575)), 46 | ), 47 | ).center(), 48 | const Gap(8), 49 | TextButton(onPressed: onRetry, child: const Text('retry').tr()), 50 | ], 51 | ); 52 | } 53 | } 54 | 55 | class ResponseLoadingWidget extends StatelessWidget { 56 | const ResponseLoadingWidget({super.key}); 57 | 58 | @override 59 | Widget build(BuildContext context) { 60 | return const Center(child: CircularProgressIndicator()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/widgets/tour/techincal_review_intro.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gap/gap.dart'; 3 | import 'package:material_symbols_icons/symbols.dart'; 4 | import 'package:styled_widget/styled_widget.dart'; 5 | 6 | class TechicalReviewIntroWidget extends StatelessWidget { 7 | const TechicalReviewIntroWidget({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Container( 12 | constraints: BoxConstraints( 13 | maxHeight: MediaQuery.of(context).size.height * 0.8, 14 | ), 15 | child: Column( 16 | children: [ 17 | Padding( 18 | padding: EdgeInsets.only(top: 16, left: 20, right: 16, bottom: 12), 19 | child: Row( 20 | children: [ 21 | Text( 22 | '技术性预览', 23 | style: Theme.of(context).textTheme.headlineSmall?.copyWith( 24 | fontWeight: FontWeight.w600, 25 | letterSpacing: -0.5, 26 | ), 27 | ), 28 | const Spacer(), 29 | IconButton( 30 | icon: const Icon(Symbols.close), 31 | onPressed: () => Navigator.pop(context), 32 | style: IconButton.styleFrom(minimumSize: const Size(36, 36)), 33 | ), 34 | ], 35 | ), 36 | ), 37 | const Divider(height: 1), 38 | Expanded( 39 | child: SingleChildScrollView( 40 | child: Column( 41 | crossAxisAlignment: CrossAxisAlignment.stretch, 42 | children: [ 43 | Text('👋').fontSize(32), 44 | Text('你好呀~').fontSize(24), 45 | Text('欢迎来使用 Solar Network 3.0 的技术性预览版。'), 46 | const Gap(24), 47 | Text('技术性预览的初衷是让我们更顺滑的将 3.0 发布出来,帮助我们一点一点的迁移数据。'), 48 | const Gap(24), 49 | Text('同时,既然是测试版,肯定有一系列的 Bug 和问题,请多多包涵,也欢迎积极反馈到 GitHub 上。'), 50 | Text('目前帐号数据已经迁移完毕,其他数据将在未来逐渐迁移。还请耐心等待,不要重复创建以免未来数据冲突。'), 51 | const Gap(24), 52 | Text('最后,感谢你愿意参与技术性预览,祝你使用愉快!'), 53 | const Gap(16), 54 | Text('关掉这个对话框就开始探索吧!').fontSize(11), 55 | ], 56 | ).padding(horizontal: 20, vertical: 24), 57 | ), 58 | ), 59 | ], 60 | ), 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/widgets/tour/tour.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_hooks/flutter_hooks.dart'; 3 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 4 | import 'package:island/services/tour.dart'; 5 | 6 | const List kStartTours = ['technical_review_intro']; 7 | 8 | class TourTriggerWidget extends HookConsumerWidget { 9 | final Widget child; 10 | const TourTriggerWidget({super.key, required this.child}); 11 | 12 | @override 13 | Widget build(BuildContext context, WidgetRef ref) { 14 | final tourStatus = ref.watch(tourStatusNotifierProvider.notifier); 15 | 16 | useEffect(() { 17 | Future(() async { 18 | for (final tour in kStartTours) { 19 | final widget = await tourStatus.showTour(tour); 20 | if (widget != null) { 21 | if (!context.mounted) return; 22 | await showModalBottomSheet( 23 | isScrollControlled: true, 24 | useRootNavigator: true, 25 | context: context, 26 | builder: (context) => widget, 27 | ); 28 | } 29 | } 30 | }); 31 | return null; 32 | }, [tourStatus]); 33 | 34 | return child; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.10) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | 12 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 13 | # which isn't available in 3.10. 14 | function(list_prepend LIST_NAME PREFIX) 15 | set(NEW_LIST "") 16 | foreach(element ${${LIST_NAME}}) 17 | list(APPEND NEW_LIST "${PREFIX}${element}") 18 | endforeach(element) 19 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 20 | endfunction() 21 | 22 | # === Flutter Library === 23 | # System-level dependencies. 24 | find_package(PkgConfig REQUIRED) 25 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 26 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 27 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 28 | 29 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 30 | 31 | # Published to parent scope for install step. 32 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 33 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 34 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 35 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 36 | 37 | list(APPEND FLUTTER_LIBRARY_HEADERS 38 | "fl_basic_message_channel.h" 39 | "fl_binary_codec.h" 40 | "fl_binary_messenger.h" 41 | "fl_dart_project.h" 42 | "fl_engine.h" 43 | "fl_json_message_codec.h" 44 | "fl_json_method_codec.h" 45 | "fl_message_codec.h" 46 | "fl_method_call.h" 47 | "fl_method_channel.h" 48 | "fl_method_codec.h" 49 | "fl_method_response.h" 50 | "fl_plugin_registrar.h" 51 | "fl_plugin_registry.h" 52 | "fl_standard_message_codec.h" 53 | "fl_standard_method_codec.h" 54 | "fl_string_codec.h" 55 | "fl_value.h" 56 | "fl_view.h" 57 | "flutter_linux.h" 58 | ) 59 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 60 | add_library(flutter INTERFACE) 61 | target_include_directories(flutter INTERFACE 62 | "${EPHEMERAL_DIR}" 63 | ) 64 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 65 | target_link_libraries(flutter INTERFACE 66 | PkgConfig::GTK 67 | PkgConfig::GLIB 68 | PkgConfig::GIO 69 | ) 70 | add_dependencies(flutter flutter_assemble) 71 | 72 | # === Flutter tool backend === 73 | # _phony_ is a non-existent file to force this command to run every time, 74 | # since currently there's no way to get a full input/output list from the 75 | # flutter tool. 76 | add_custom_command( 77 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 78 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 79 | COMMAND ${CMAKE_COMMAND} -E env 80 | ${FLUTTER_TOOL_ENVIRONMENT} 81 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 82 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 83 | VERBATIM 84 | ) 85 | add_custom_target(flutter_assemble DEPENDS 86 | "${FLUTTER_LIBRARY}" 87 | ${FLUTTER_LIBRARY_HEADERS} 88 | ) 89 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | bitsdojo_window_linux 7 | file_selector_linux 8 | flutter_platform_alert 9 | flutter_udid 10 | flutter_webrtc 11 | irondash_engine_context 12 | media_kit_libs_linux 13 | media_kit_video 14 | pasteboard 15 | record_linux 16 | sqlite3_flutter_libs 17 | super_native_extensions 18 | url_launcher_linux 19 | volume_controller 20 | ) 21 | 22 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 23 | croppy 24 | ) 25 | 26 | set(PLUGIN_BUNDLED_LIBRARIES) 27 | 28 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 29 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 30 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 31 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 32 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 33 | endforeach(plugin) 34 | 35 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 36 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 37 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 38 | endforeach(ffi_plugin) 39 | -------------------------------------------------------------------------------- /linux/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} 10 | "main.cc" 11 | "my_application.cc" 12 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 13 | ) 14 | 15 | # Apply the standard set of build settings. This can be removed for applications 16 | # that need different build settings. 17 | apply_standard_settings(${BINARY_NAME}) 18 | 19 | # Add preprocessor definitions for the application ID. 20 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 21 | 22 | # Add dependency libraries. Add any application-specific dependencies here. 23 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 24 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 25 | 26 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 27 | -------------------------------------------------------------------------------- /linux/runner/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /linux/runner/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import bitsdojo_window_macos 9 | import connectivity_plus 10 | import device_info_plus 11 | import file_picker 12 | import file_selector_macos 13 | import firebase_core 14 | import firebase_messaging 15 | import flutter_inappwebview_macos 16 | import flutter_platform_alert 17 | import flutter_udid 18 | import flutter_webrtc 19 | import irondash_engine_context 20 | import livekit_client 21 | import media_kit_libs_macos_video 22 | import media_kit_video 23 | import package_info_plus 24 | import pasteboard 25 | import path_provider_foundation 26 | import record_macos 27 | import shared_preferences_foundation 28 | import sqflite_darwin 29 | import sqlite3_flutter_libs 30 | import super_native_extensions 31 | import url_launcher_macos 32 | import volume_controller 33 | import wakelock_plus 34 | 35 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 36 | BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin")) 37 | ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) 38 | DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) 39 | FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) 40 | FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) 41 | FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) 42 | FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) 43 | InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) 44 | FlutterPlatformAlertPlugin.register(with: registry.registrar(forPlugin: "FlutterPlatformAlertPlugin")) 45 | FlutterUdidPlugin.register(with: registry.registrar(forPlugin: "FlutterUdidPlugin")) 46 | FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin")) 47 | IrondashEngineContextPlugin.register(with: registry.registrar(forPlugin: "IrondashEngineContextPlugin")) 48 | LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin")) 49 | MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin")) 50 | MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin")) 51 | FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) 52 | PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) 53 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 54 | RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin")) 55 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) 56 | SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) 57 | Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) 58 | SuperNativeExtensionsPlugin.register(with: registry.registrar(forPlugin: "SuperNativeExtensionsPlugin")) 59 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) 60 | VolumeControllerPlugin.register(with: registry.registrar(forPlugin: "VolumeControllerPlugin")) 61 | WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) 62 | } 63 | -------------------------------------------------------------------------------- /macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.15' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | 32 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 33 | target 'RunnerTests' do 34 | inherit! :search_paths 35 | end 36 | end 37 | 38 | post_install do |installer| 39 | installer.pods_project.targets.each do |target| 40 | flutter_additional_macos_build_settings(target) 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @main 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | 10 | override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { 11 | return true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "version": 1, 4 | "author": "xcode" 5 | }, 6 | "images": [ 7 | { 8 | "size": "16x16", 9 | "idiom": "mac", 10 | "filename": "app_icon_16.png", 11 | "scale": "1x" 12 | }, 13 | { 14 | "size": "16x16", 15 | "idiom": "mac", 16 | "filename": "app_icon_32.png", 17 | "scale": "2x" 18 | }, 19 | { 20 | "size": "32x32", 21 | "idiom": "mac", 22 | "filename": "app_icon_32.png", 23 | "scale": "1x" 24 | }, 25 | { 26 | "size": "32x32", 27 | "idiom": "mac", 28 | "filename": "app_icon_64.png", 29 | "scale": "2x" 30 | }, 31 | { 32 | "size": "128x128", 33 | "idiom": "mac", 34 | "filename": "app_icon_128.png", 35 | "scale": "1x" 36 | }, 37 | { 38 | "size": "128x128", 39 | "idiom": "mac", 40 | "filename": "app_icon_256.png", 41 | "scale": "2x" 42 | }, 43 | { 44 | "size": "256x256", 45 | "idiom": "mac", 46 | "filename": "app_icon_256.png", 47 | "scale": "1x" 48 | }, 49 | { 50 | "size": "256x256", 51 | "idiom": "mac", 52 | "filename": "app_icon_512.png", 53 | "scale": "2x" 54 | }, 55 | { 56 | "size": "512x512", 57 | "idiom": "mac", 58 | "filename": "app_icon_512.png", 59 | "scale": "1x" 60 | }, 61 | { 62 | "size": "512x512", 63 | "idiom": "mac", 64 | "filename": "app_icon_1024.png", 65 | "scale": "2x" 66 | } 67 | ] 68 | } -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = island 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = dev.solsynth.solian 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.device.audio-input 10 | 11 | com.apple.security.device.camera 12 | 13 | com.apple.security.files.downloads.read-write 14 | 15 | com.apple.security.files.user-selected.read-only 16 | 17 | com.apple.security.network.client 18 | 19 | com.apple.security.network.server 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /macos/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | API_KEY 6 | AIzaSyCzQIyiYKoYHTpGXhN-IjgMML8z797WVD8 7 | GCM_SENDER_ID 8 | 961776991058 9 | PLIST_VERSION 10 | 1 11 | BUNDLE_ID 12 | dev.solsynth.solian 13 | PROJECT_ID 14 | solian-0x001 15 | STORAGE_BUCKET 16 | solian-0x001.firebasestorage.app 17 | IS_ADS_ENABLED 18 | 19 | IS_ANALYTICS_ENABLED 20 | 21 | IS_APPINVITE_ENABLED 22 | 23 | IS_GCM_ENABLED 24 | 25 | IS_SIGNIN_ENABLED 26 | 27 | GOOGLE_APP_ID 28 | 1:961776991058:ios:727229d368cc47e1f4188b 29 | 30 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ITSAppUsesNonExemptEncryption 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleExecutable 10 | Solian 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | Solian 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | $(FLUTTER_BUILD_NAME) 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | $(PRODUCT_COPYRIGHT) 29 | NSMainNibFile 30 | MainMenu 31 | LSApplicationCategoryType 32 | public.app-category.social-networking 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | import bitsdojo_window_macos 4 | 5 | class MainFlutterWindow: BitsdojoWindow { 6 | override func bitsdojo_window_configure() -> UInt { 7 | return BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP 8 | } 9 | 10 | override func awakeFromNib() { 11 | let flutterViewController = FlutterViewController() 12 | let windowFrame = self.frame 13 | self.contentViewController = flutterViewController 14 | self.setFrame(windowFrame, display: true) 15 | 16 | RegisterGeneratedPlugins(registry: flutterViewController) 17 | 18 | super.awakeFromNib() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.device.audio-input 8 | 9 | com.apple.security.device.camera 10 | 11 | com.apple.security.files.downloads.read-write 12 | 13 | com.apple.security.files.user-selected.read-only 14 | 15 | com.apple.security.network.client 16 | 17 | com.apple.security.network.server 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /web/_redirects: -------------------------------------------------------------------------------- 1 | /assets/assets/i18n/en.json /assets/assets/i18n/en-US.json 301 2 | /assets/assets/i18n/zh.json /assets/assets/i18n/zh-CN.json 301 3 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Solian", 3 | "short_name": "Solian", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#ffffff", 7 | "theme_color": "#7d80ba", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /web/splash/img/dark-1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/web/splash/img/dark-1x.png -------------------------------------------------------------------------------- /web/splash/img/dark-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/web/splash/img/dark-2x.png -------------------------------------------------------------------------------- /web/splash/img/dark-3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/web/splash/img/dark-3x.png -------------------------------------------------------------------------------- /web/splash/img/dark-4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/web/splash/img/dark-4x.png -------------------------------------------------------------------------------- /web/splash/img/light-1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/web/splash/img/light-1x.png -------------------------------------------------------------------------------- /web/splash/img/light-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/web/splash/img/light-2x.png -------------------------------------------------------------------------------- /web/splash/img/light-3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/web/splash/img/light-3x.png -------------------------------------------------------------------------------- /web/splash/img/light-4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/web/splash/img/light-4x.png -------------------------------------------------------------------------------- /web/sqlite3.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/web/sqlite3.wasm -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | bitsdojo_window_windows 7 | connectivity_plus 8 | file_selector_windows 9 | firebase_core 10 | flutter_inappwebview_windows 11 | flutter_platform_alert 12 | flutter_udid 13 | flutter_webrtc 14 | irondash_engine_context 15 | livekit_client 16 | media_kit_libs_windows_video 17 | media_kit_video 18 | pasteboard 19 | record_windows 20 | sqlite3_flutter_libs 21 | super_native_extensions 22 | url_launcher_windows 23 | volume_controller 24 | ) 25 | 26 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 27 | croppy 28 | ) 29 | 30 | set(PLUGIN_BUNDLED_LIBRARIES) 31 | 32 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 33 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 34 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 35 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 36 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 37 | endforeach(plugin) 38 | 39 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 40 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 41 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 42 | endforeach(ffi_plugin) 43 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | #include 9 | auto bdw = bitsdojo_window_configure(BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP); 10 | 11 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 12 | _In_ wchar_t *command_line, _In_ int show_command) { 13 | // Attach to console when present (e.g., 'flutter run') or create a 14 | // new console when running with a debugger. 15 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 16 | CreateAndAttachConsole(); 17 | } 18 | 19 | // Initialize COM, so that it is available for use in the library and/or 20 | // plugins. 21 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 22 | 23 | flutter::DartProject project(L"data"); 24 | 25 | std::vector command_line_arguments = 26 | GetCommandLineArguments(); 27 | 28 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 29 | 30 | FlutterWindow window(project); 31 | Win32Window::Point origin(10, 10); 32 | Win32Window::Size size(1280, 720); 33 | if (!window.Create(L"Solian", origin, size)) { 34 | return EXIT_FAILURE; 35 | } 36 | window.SetQuitOnClose(true); 37 | 38 | ::MSG msg; 39 | while (::GetMessage(&msg, nullptr, 0, 0)) { 40 | ::TranslateMessage(&msg); 41 | ::DispatchMessage(&msg); 42 | } 43 | 44 | ::CoUninitialize(); 45 | return EXIT_SUCCESS; 46 | } 47 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Solsynth/Solian/b84fafb53c0b843a7bc67d3dfda341d96989cc82/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | unsigned int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length == 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | --------------------------------------------------------------------------------