Compare commits
45 Commits
9cc577adbe
...
2.4.2+86
Author | SHA1 | Date | |
---|---|---|---|
|
69d5e95565 | ||
|
3e3442fc89 | ||
|
8181010b0b | ||
|
269caf7555 | ||
|
ae0809ad35 | ||
|
4005f03cf8 | ||
|
4bd8ec54f1 | ||
|
51a387851f | ||
|
8ed847d870 | ||
|
dfe13de220 | ||
|
b02a54c1e9 | ||
|
55a7e7d900 | ||
|
3585941ccb | ||
|
7c6f2cc4ab | ||
|
61dbf92909 | ||
|
b69e4002e0 | ||
|
49aa24b79d | ||
|
ceb5c53229 | ||
|
908f0cb59e | ||
|
7c2b8de931 | ||
|
ddd0a4c3d3 | ||
|
99e07de243 | ||
|
6bb9c21759 | ||
|
8f2fc55608 | ||
|
a1c4e5eca0 | ||
|
10bf0883e5 | ||
|
595050f89f | ||
|
0722c99f21 | ||
|
12d03836f9 | ||
|
f78d3f4fd5 | ||
|
e798a8ba76 | ||
|
c28a664373 | ||
|
4589722c3b | ||
|
38e1c51b45 | ||
|
610ddec05c | ||
|
d0276f9ac6 | ||
|
c1e89a2ee6 | ||
|
ecc79368a1 | ||
|
e6d732c86a | ||
|
dd055fb077 | ||
|
280840c6d8 | ||
|
bde62a7b2c | ||
|
5445c570a2 | ||
|
b2302f5b3c | ||
|
d7359cfd0d |
26
.github/workflows/nightly.yml
vendored
26
.github/workflows/nightly.yml
vendored
@@ -48,14 +48,15 @@ jobs:
|
|||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
channel: stable
|
channel: stable
|
||||||
cache: true
|
|
||||||
- run: |
|
- run: |
|
||||||
sudo apt-get update -y
|
sudo apt-get update -y
|
||||||
sudo apt-get install -y ninja-build libgtk-3-dev
|
sudo apt-get install -y ninja-build libgtk-3-dev
|
||||||
sudo apt-get install libmpv-dev mpv
|
sudo apt-get install -y libmpv-dev mpv
|
||||||
sudo apt-get install libayatana-appindicator3-dev
|
sudo apt-get install -y libayatana-appindicator3-dev
|
||||||
sudo apt-get install keybinder-3.0
|
sudo apt-get install -y keybinder-3.0
|
||||||
sudo apt-get install libnotify-dev
|
sudo apt-get install -y libnotify-dev
|
||||||
|
sudo apt-get install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
|
||||||
|
sudo apt-get install -y gstreamer-1.0
|
||||||
- run: flutter pub get
|
- run: flutter pub get
|
||||||
- run: flutter build linux
|
- run: flutter build linux
|
||||||
- name: Archive production artifacts
|
- name: Archive production artifacts
|
||||||
@@ -63,3 +64,18 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: build-output-linux
|
name: build-output-linux
|
||||||
path: build/linux/x64/release/bundle
|
path: build/linux/x64/release/bundle
|
||||||
|
- name: Build AppImage
|
||||||
|
run: |
|
||||||
|
rm -r Solian.AppDir | true
|
||||||
|
mkdir Solian.AppDir
|
||||||
|
cp -r build/linux/x64/release/bundle/* Solian.AppDir
|
||||||
|
cp -r buildtools/appimage_config/* Solian.AppDir
|
||||||
|
cp assets/icon/icon-light-radius.png Solian.AppDir
|
||||||
|
sudo chmod +x buildtools/appimagetool-x86_64.AppImage
|
||||||
|
sudo chmod +x Solian.AppDir/AppRun
|
||||||
|
./buildtools/appimagetool-x86_64.AppImage Solian.AppDir
|
||||||
|
- name: Archive production artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: build-output-linux-appimage
|
||||||
|
path: './*.AppImage*'
|
||||||
|
BIN
assets/audio/notify/metal-pipe.mp3
Normal file
BIN
assets/audio/notify/metal-pipe.mp3
Normal file
Binary file not shown.
BIN
assets/audio/sfx/launch-done.mp3
Normal file
BIN
assets/audio/sfx/launch-done.mp3
Normal file
Binary file not shown.
BIN
assets/audio/sfx/launch-intro.mp3
Normal file
BIN
assets/audio/sfx/launch-intro.mp3
Normal file
Binary file not shown.
@@ -639,6 +639,7 @@
|
|||||||
"postQuestionUnansweredWithReward": "Unanswered Question, reward source points {}",
|
"postQuestionUnansweredWithReward": "Unanswered Question, reward source points {}",
|
||||||
"postQuestionAnswered": "Answered Question",
|
"postQuestionAnswered": "Answered Question",
|
||||||
"postQuestionAnswerSelect": "Select as Answer",
|
"postQuestionAnswerSelect": "Select as Answer",
|
||||||
|
"postQuestionAnswerTitle": "Selected Question",
|
||||||
"postQuestionAnswerSelected": "Answer has been selected, reward has been applied.",
|
"postQuestionAnswerSelected": "Answer has been selected, reward has been applied.",
|
||||||
"postVideoUpload": "Upload Video",
|
"postVideoUpload": "Upload Video",
|
||||||
"realmJoin": "Join Realm",
|
"realmJoin": "Join Realm",
|
||||||
@@ -915,5 +916,37 @@
|
|||||||
"accountProgramJoined": "Joined Program.",
|
"accountProgramJoined": "Joined Program.",
|
||||||
"accountProgramAlreadyJoined": "Joined",
|
"accountProgramAlreadyJoined": "Joined",
|
||||||
"accountProgramLeft": "Left Program.",
|
"accountProgramLeft": "Left Program.",
|
||||||
"leave": "Leave"
|
"leave": "Leave",
|
||||||
|
"attachmentFailedToLoadMedia": "Unable to load media file, please try again later. If this error occurs repeatedly, the source file may not exist or the network connection may be abnormal.",
|
||||||
|
"accountPunishments": "Punishments",
|
||||||
|
"accountPunishmentsDescription": "View your account's reputation status.",
|
||||||
|
"punishmentType0": "Strike",
|
||||||
|
"punishmentType1": "Limited",
|
||||||
|
"punishmentType2": "Banned",
|
||||||
|
"punishmentOverall": "Overall Status",
|
||||||
|
"punishmentStatusNormal": "All abilities normal",
|
||||||
|
"punishmentStatusWarned": "All abilities normal, but at least one strike is in effect",
|
||||||
|
"punishmentStatusLimited": "Some abilities limited, at least one limited punishment is in effect",
|
||||||
|
"punishmentStatusLimitedFully": "All abilities limited, at least one completely limited punishment is in effect",
|
||||||
|
"punishmentStatusBanned": "All services are terminated, banned",
|
||||||
|
"punishmentCreatedAt": "Applied since {}",
|
||||||
|
"punishmentExpiredAt": "Expired at {}",
|
||||||
|
"punishmentExpiredNever": "Never expired",
|
||||||
|
"punishmentModerator": "Moderator who made this punishment",
|
||||||
|
"punishmentMadeBySystem": "Made by auto-mod system",
|
||||||
|
"settingsAprilFoolFeatures": "April Fool Features",
|
||||||
|
"settingsAprilFoolFeaturesDescription": "Enable April Fool features during April Fool, this option will only be visible during April Fool.",
|
||||||
|
"settingsSoundEffects": "Sound Effects",
|
||||||
|
"settingsSoundEffectsDescription": "Enable the sound effects around the app.",
|
||||||
|
"settingsResetMemorizedWindowSize": "Reset Window Size",
|
||||||
|
"settingsResetMemorizedWindowSizeDescription": "Reset the memorized window size, and set it to the default size.",
|
||||||
|
"chatDirect": "Direct Messages",
|
||||||
|
"back": "Back",
|
||||||
|
"badgeProgramDeveloper": "Developer Program Member",
|
||||||
|
"badgeProgramStellar": "A Stellar",
|
||||||
|
"badgeProgramModerator": "Community Moderator",
|
||||||
|
"postEditedHint": "edited",
|
||||||
|
"splashScreenServer": "Server",
|
||||||
|
"splashScreenServerName": "Potato",
|
||||||
|
"splashScreenCaption": "Trying to establishing connection with HyperNet™"
|
||||||
}
|
}
|
||||||
|
@@ -913,5 +913,37 @@
|
|||||||
"accountProgramJoined": "已加入计划。",
|
"accountProgramJoined": "已加入计划。",
|
||||||
"accountProgramLeft": "已离开计划。",
|
"accountProgramLeft": "已离开计划。",
|
||||||
"accountProgramAlreadyJoined": "已加入",
|
"accountProgramAlreadyJoined": "已加入",
|
||||||
"leave": "离开"
|
"leave": "离开",
|
||||||
|
"attachmentFailedToLoadMedia": "无法加载媒体文件,请稍后重试。若此错误重复出现,可能源文件不存在或者网络连接异常。",
|
||||||
|
"accountPunishments": "处分",
|
||||||
|
"accountPunishmentsDescription": "查看你帐号的信誉状态。",
|
||||||
|
"punishmentType0": "警告",
|
||||||
|
"punishmentType1": "停权",
|
||||||
|
"punishmentType2": "封禁",
|
||||||
|
"punishmentOverall": "总体状态",
|
||||||
|
"punishmentStatusNormal": "所有功能正常",
|
||||||
|
"punishmentStatusWarned": "所有功能正常,但有警告生效",
|
||||||
|
"punishmentStatusLimited": "部份功能暂时受限,有至少一个停权生效",
|
||||||
|
"punishmentStatusLimitedFully": "所有功能暂时受限,有至少一个完全停权生效",
|
||||||
|
"punishmentStatusBanned": "所有服务终止,已被封禁",
|
||||||
|
"punishmentCreatedAt": "宣布于 {}",
|
||||||
|
"punishmentExpiredAt": "到期于 {}",
|
||||||
|
"punishmentExpiredNever": "永久生效",
|
||||||
|
"punishmentModerator": "责任管理员",
|
||||||
|
"punishmentMadeBySystem": "由系统自动裁决",
|
||||||
|
"settingsAprilFoolFeatures": "愚人节特性",
|
||||||
|
"settingsAprilFoolFeaturesDescription": "在愚人节期间启用愚人节特性,该选项只会在愚人节期间显示。",
|
||||||
|
"settingsSoundEffects": "声音效果",
|
||||||
|
"settingsSoundEffectsDescription": "在一些场合下启用声音特效。",
|
||||||
|
"settingsResetMemorizedWindowSize": "重置窗口大小",
|
||||||
|
"settingsResetMemorizedWindowSizeDescription": "重置记忆的窗口大小,以重新设置为默认大小。",
|
||||||
|
"chatDirect": "私信",
|
||||||
|
"back": "返回",
|
||||||
|
"badgeProgramDeveloper": "开发者计划成员",
|
||||||
|
"badgeProgramStellar": "一颗恒星",
|
||||||
|
"badgeProgramModerator": "社区管理员",
|
||||||
|
"postEditedHint": "已编辑",
|
||||||
|
"splashScreenServer": "服务器",
|
||||||
|
"splashScreenServerName": "土豆",
|
||||||
|
"splashScreenCaption": "正在尝试与 HyperNet™ 取得太阳链连接"
|
||||||
}
|
}
|
||||||
|
4
buildtools/appimage_config/AppRun
Executable file
4
buildtools/appimage_config/AppRun
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
exec ./surface
|
8
buildtools/appimage_config/Solian.desktop
Normal file
8
buildtools/appimage_config/Solian.desktop
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Type=Application
|
||||||
|
Terminal=false
|
||||||
|
Name=Solian
|
||||||
|
Exec=surface %u
|
||||||
|
Icon=icon-light-radius
|
||||||
|
Categories=Network;
|
BIN
buildtools/appimagetool-x86_64.AppImage
Executable file
BIN
buildtools/appimagetool-x86_64.AppImage
Executable file
Binary file not shown.
101
ios/Podfile.lock
101
ios/Podfile.lock
@@ -1,5 +1,7 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- Alamofire (5.10.2)
|
- Alamofire (5.10.2)
|
||||||
|
- audioplayers_darwin (0.0.1):
|
||||||
|
- Flutter
|
||||||
- connectivity_plus (0.0.1):
|
- connectivity_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- croppy (0.0.1):
|
- croppy (0.0.1):
|
||||||
@@ -187,9 +189,12 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- flutter_webrtc
|
- flutter_webrtc
|
||||||
- WebRTC-SDK (= 125.6422.06)
|
- WebRTC-SDK (= 125.6422.06)
|
||||||
- media_kit_libs_ios_video (1.0.4):
|
- livekit_noise_filter (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- media_kit_native_event_loop (1.0.0):
|
- flutter_webrtc
|
||||||
|
- LiveKitKrispNoiseFilter (= 0.0.7)
|
||||||
|
- LiveKitKrispNoiseFilter (0.0.7)
|
||||||
|
- media_kit_libs_ios_video (1.0.4):
|
||||||
- Flutter
|
- Flutter
|
||||||
- media_kit_video (0.0.1):
|
- media_kit_video (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
@@ -212,8 +217,6 @@ PODS:
|
|||||||
- receive_sharing_intent (1.8.1):
|
- receive_sharing_intent (1.8.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- SAMKeychain (1.5.3)
|
- SAMKeychain (1.5.3)
|
||||||
- screen_brightness_ios (0.1.0):
|
|
||||||
- Flutter
|
|
||||||
- SDWebImage (5.20.1):
|
- SDWebImage (5.20.1):
|
||||||
- SDWebImage/Core (= 5.20.1)
|
- SDWebImage/Core (= 5.20.1)
|
||||||
- SDWebImage/Core (5.20.1)
|
- SDWebImage/Core (5.20.1)
|
||||||
@@ -262,6 +265,7 @@ PODS:
|
|||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- Alamofire
|
- Alamofire
|
||||||
|
- audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/ios`)
|
||||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||||
- croppy (from `.symlinks/plugins/croppy/ios`)
|
- croppy (from `.symlinks/plugins/croppy/ios`)
|
||||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
@@ -284,15 +288,14 @@ DEPENDENCIES:
|
|||||||
- in_app_review (from `.symlinks/plugins/in_app_review/ios`)
|
- in_app_review (from `.symlinks/plugins/in_app_review/ios`)
|
||||||
- Kingfisher (~> 8.0)
|
- Kingfisher (~> 8.0)
|
||||||
- livekit_client (from `.symlinks/plugins/livekit_client/ios`)
|
- livekit_client (from `.symlinks/plugins/livekit_client/ios`)
|
||||||
|
- livekit_noise_filter (from `.symlinks/plugins/livekit_noise_filter/ios`)
|
||||||
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
|
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
|
||||||
- media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`)
|
|
||||||
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
|
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- pasteboard (from `.symlinks/plugins/pasteboard/ios`)
|
- pasteboard (from `.symlinks/plugins/pasteboard/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
- receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`)
|
- receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`)
|
||||||
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
|
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
|
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
|
||||||
@@ -318,6 +321,7 @@ SPEC REPOS:
|
|||||||
- GoogleDataTransport
|
- GoogleDataTransport
|
||||||
- GoogleUtilities
|
- GoogleUtilities
|
||||||
- Kingfisher
|
- Kingfisher
|
||||||
|
- LiveKitKrispNoiseFilter
|
||||||
- nanopb
|
- nanopb
|
||||||
- OrderedSet
|
- OrderedSet
|
||||||
- PromisesObjC
|
- PromisesObjC
|
||||||
@@ -328,6 +332,8 @@ SPEC REPOS:
|
|||||||
- WebRTC-SDK
|
- WebRTC-SDK
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
|
audioplayers_darwin:
|
||||||
|
:path: ".symlinks/plugins/audioplayers_darwin/ios"
|
||||||
connectivity_plus:
|
connectivity_plus:
|
||||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||||
croppy:
|
croppy:
|
||||||
@@ -370,10 +376,10 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/in_app_review/ios"
|
:path: ".symlinks/plugins/in_app_review/ios"
|
||||||
livekit_client:
|
livekit_client:
|
||||||
:path: ".symlinks/plugins/livekit_client/ios"
|
:path: ".symlinks/plugins/livekit_client/ios"
|
||||||
|
livekit_noise_filter:
|
||||||
|
:path: ".symlinks/plugins/livekit_noise_filter/ios"
|
||||||
media_kit_libs_ios_video:
|
media_kit_libs_ios_video:
|
||||||
:path: ".symlinks/plugins/media_kit_libs_ios_video/ios"
|
:path: ".symlinks/plugins/media_kit_libs_ios_video/ios"
|
||||||
media_kit_native_event_loop:
|
|
||||||
:path: ".symlinks/plugins/media_kit_native_event_loop/ios"
|
|
||||||
media_kit_video:
|
media_kit_video:
|
||||||
:path: ".symlinks/plugins/media_kit_video/ios"
|
:path: ".symlinks/plugins/media_kit_video/ios"
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
@@ -386,8 +392,6 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||||
receive_sharing_intent:
|
receive_sharing_intent:
|
||||||
:path: ".symlinks/plugins/receive_sharing_intent/ios"
|
:path: ".symlinks/plugins/receive_sharing_intent/ios"
|
||||||
screen_brightness_ios:
|
|
||||||
:path: ".symlinks/plugins/screen_brightness_ios/ios"
|
|
||||||
share_plus:
|
share_plus:
|
||||||
:path: ".symlinks/plugins/share_plus/ios"
|
:path: ".symlinks/plugins/share_plus/ios"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
@@ -409,65 +413,66 @@ EXTERNAL SOURCES:
|
|||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
|
Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
|
||||||
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
|
audioplayers_darwin: ccf9c770ee768abb07e26d90af093f7bab1c12ab
|
||||||
croppy: b6199bc8d56bd2e03cc11609d1c47ad9875c1321
|
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
||||||
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
|
croppy: 979e8ddc254f4642bffe7d52dc7193354b27ba30
|
||||||
|
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
|
||||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||||
fast_rsa: dc48fb05f26bb108863de122b2a9f5554e8e2591
|
fast_rsa: d99f8e1809a4a312fa9216d830186869b2e9eb65
|
||||||
file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49
|
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
||||||
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
|
file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6
|
||||||
Firebase: d80354ed7f6df5f9aca55e9eb47cc4b634735eaf
|
Firebase: d80354ed7f6df5f9aca55e9eb47cc4b634735eaf
|
||||||
firebase_analytics: e3b6782e70e32b7fa18f7cd233e3201975dd86aa
|
firebase_analytics: 4e93dbe66872104d28ae9750fec1800e1fd66858
|
||||||
firebase_core: ac395f994af4e28f6a38b59e05a88ca57abeb874
|
firebase_core: 8d552814f6c01ccde5d88939fced4ec26f2f5510
|
||||||
firebase_messaging: 7e223f4ee7ca053bf4ce43748e84a6d774ec9728
|
firebase_messaging: 8b96a4f09841c15a16b96973ef5c3dcfc1a064e4
|
||||||
FirebaseAnalytics: 4fd42def128146e24e480e89f310e3d8534ea42b
|
FirebaseAnalytics: 4fd42def128146e24e480e89f310e3d8534ea42b
|
||||||
FirebaseCore: 99fe0c4b44a39f37d99e6404e02009d2db5d718d
|
FirebaseCore: 99fe0c4b44a39f37d99e6404e02009d2db5d718d
|
||||||
FirebaseCoreInternal: df24ce5af28864660ecbd13596fc8dd3a8c34629
|
FirebaseCoreInternal: df24ce5af28864660ecbd13596fc8dd3a8c34629
|
||||||
FirebaseInstallations: 6c963bd2a86aca0481eef4f48f5a4df783ae5917
|
FirebaseInstallations: 6c963bd2a86aca0481eef4f48f5a4df783ae5917
|
||||||
FirebaseMessaging: 487b634ccdf6f7b7ff180fdcb2a9935490f764e8
|
FirebaseMessaging: 487b634ccdf6f7b7ff180fdcb2a9935490f764e8
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
flutter_app_update: 65f61da626cb111d1b24674abc4b01728d7723bc
|
flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9
|
||||||
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
|
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
||||||
flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29
|
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
||||||
flutter_timezone: ac3da59ac941ff1c98a2e1f0293420e020120282
|
flutter_timezone: 7c838e17ffd4645d261e87037e5bebf6d38fe544
|
||||||
flutter_udid: b2417673f287ee62817a1de3d1643f47b9f508ab
|
flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9
|
||||||
flutter_webrtc: 90260f83024b1b96d239a575ea4e3708e79344d1
|
flutter_webrtc: 57f32415b8744e806f9c2a96ccdb60c6a627ba33
|
||||||
gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5
|
gal: baecd024ebfd13c441269ca7404792a7152fde89
|
||||||
GoogleAppMeasurement: fc0817122bd4d4189164f85374e06773b9561896
|
GoogleAppMeasurement: fc0817122bd4d4189164f85374e06773b9561896
|
||||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||||
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
||||||
home_widget: 0434835a4c9a75704264feff6be17ea40e0f0d57
|
home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f
|
||||||
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
|
||||||
in_app_review: a31b5257259646ea78e0e35fc914979b0031d011
|
in_app_review: 5596fe56fab799e8edb3561c03d053363ab13457
|
||||||
Kingfisher: 323e5c4ec7983aaace12af655a7b51a7f88a599d
|
Kingfisher: 323e5c4ec7983aaace12af655a7b51a7f88a599d
|
||||||
livekit_client: 170022ce5f7c8c70d7f862ac9c17e11508ad5fbc
|
livekit_client: 08755cabfa4da4ed455642f460cfbb39bc518070
|
||||||
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
|
livekit_noise_filter: a26aeb1c1eae6db0a023fd2f6ea3ff108c3ecbb0
|
||||||
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
|
LiveKitKrispNoiseFilter: efe418ceca28163ace0ff222bd2cc02384645d84
|
||||||
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
|
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
|
||||||
|
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
|
||||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||||
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||||
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
|
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||||
pasteboard: 982969ebaa7c78af3e6cc7761e8f5e77565d9ce0
|
pasteboard: 49088aeb6119d51f976a421db60d8e1ab079b63c
|
||||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||||
receive_sharing_intent: 79c848f5b045674ad60b9fea3bafea59962ad2c1
|
receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00
|
||||||
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
||||||
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
|
|
||||||
SDWebImage: 33d0f23bddeb5d209ae959153883247be6703713
|
SDWebImage: 33d0f23bddeb5d209ae959153883247be6703713
|
||||||
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
|
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
|
||||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
|
||||||
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||||
sqlite3: fc1400008a9b3525f5914ed715a5d1af0b8f4983
|
sqlite3: fc1400008a9b3525f5914ed715a5d1af0b8f4983
|
||||||
sqlite3_flutter_libs: 487032b9008b28de37c72a3aa66849ef3745f3e6
|
sqlite3_flutter_libs: f6acaa2172e6bb3e2e70c771661905080e8ebcf2
|
||||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
|
||||||
video_compress: fce97e4fb1dfd88175aa07d2ffc8a2f297f87fbe
|
video_compress: f2133a07762889d67f0711ac831faa26f956980e
|
||||||
volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9
|
volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12
|
||||||
wakelock_plus: 373cfe59b235a6dd5837d0fb88791d2f13a90d56
|
wakelock_plus: 04623e3f525556020ebd4034310f20fe7fda8b49
|
||||||
WebRTC-SDK: 79942c006ea64f6fb48d7da8a4786dfc820bc1db
|
WebRTC-SDK: 79942c006ea64f6fb48d7da8a4786dfc820bc1db
|
||||||
workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6
|
workmanager: 01be2de7f184bd15de93a1812936a2b7f42ef07e
|
||||||
|
|
||||||
PODFILE CHECKSUM: 9b244e02f87527430136c8d21cbdcf1cd586b6bc
|
PODFILE CHECKSUM: 9b244e02f87527430136c8d21cbdcf1cd586b6bc
|
||||||
|
|
||||||
|
@@ -241,7 +241,7 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
contentController.text = post.body['content'] ?? '';
|
contentController.text = post.body['content'] ?? '';
|
||||||
aliasController.text = post.alias ?? '';
|
aliasController.text = post.alias ?? '';
|
||||||
rewardController.text = post.body['reward']?.toString() ?? '';
|
rewardController.text = post.body['reward']?.toString() ?? '';
|
||||||
videoAttachment = post.preload?.video;
|
videoAttachment = SnAttachment.fromJson(post.body['video']);
|
||||||
publishedAt = post.publishedAt;
|
publishedAt = post.publishedAt;
|
||||||
publishedUntil = post.publishedUntil;
|
publishedUntil = post.publishedUntil;
|
||||||
visibleUsers = List.from(post.visibleUsersList ?? [], growable: true);
|
visibleUsers = List.from(post.visibleUsersList ?? [], growable: true);
|
||||||
@@ -252,17 +252,21 @@ class PostWriteController extends ChangeNotifier {
|
|||||||
categories =
|
categories =
|
||||||
List.from(post.categories.map((ele) => ele.alias), growable: true);
|
List.from(post.categories.map((ele) => ele.alias), growable: true);
|
||||||
attachments.addAll(
|
attachments.addAll(
|
||||||
post.preload?.attachments?.map((ele) => PostWriteMedia(ele)) ?? []);
|
post.body['attachments']
|
||||||
poll = post.preload?.poll;
|
.where(SnAttachment.fromJson)
|
||||||
|
?.map(PostWriteMedia) ??
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
poll = post.poll;
|
||||||
|
|
||||||
editingDraft = post.isDraft;
|
editingDraft = post.isDraft;
|
||||||
|
|
||||||
if (post.preload?.thumbnail != null &&
|
if (post.body['thumbnail'] != null) {
|
||||||
(post.preload?.thumbnail?.rid.isNotEmpty ?? false)) {
|
thumbnail =
|
||||||
thumbnail = PostWriteMedia(post.preload!.thumbnail);
|
PostWriteMedia(SnAttachment.fromJson(post.body['thumbnail']));
|
||||||
}
|
}
|
||||||
if (post.preload?.realm != null) {
|
if (post.realm != null) {
|
||||||
realm = post.preload!.realm!;
|
realm = post.realm!;
|
||||||
}
|
}
|
||||||
|
|
||||||
editingPost = post;
|
editingPost = post;
|
||||||
|
360
lib/main.dart
360
lib/main.dart
@@ -3,6 +3,7 @@ import 'dart:developer';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:audioplayers/audioplayers.dart';
|
||||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||||
import 'package:croppy/croppy.dart';
|
import 'package:croppy/croppy.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
@@ -14,6 +15,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@@ -48,6 +50,7 @@ import 'package:surface/router.dart';
|
|||||||
import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
|
import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/menu_bar.dart';
|
import 'package:surface/widgets/menu_bar.dart';
|
||||||
|
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||||
import 'package:surface/widgets/version_label.dart';
|
import 'package:surface/widgets/version_label.dart';
|
||||||
import 'package:tray_manager/tray_manager.dart';
|
import 'package:tray_manager/tray_manager.dart';
|
||||||
import 'package:version/version.dart';
|
import 'package:version/version.dart';
|
||||||
@@ -56,6 +59,7 @@ import 'package:in_app_review/in_app_review.dart';
|
|||||||
import 'package:image_picker_android/image_picker_android.dart';
|
import 'package:image_picker_android/image_picker_android.dart';
|
||||||
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
|
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
|
||||||
import 'package:local_notifier/local_notifier.dart';
|
import 'package:local_notifier/local_notifier.dart';
|
||||||
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
|
|
||||||
@pragma('vm:entry-point')
|
@pragma('vm:entry-point')
|
||||||
void appBackgroundDispatcher() {
|
void appBackgroundDispatcher() {
|
||||||
@@ -74,13 +78,40 @@ void appBackgroundDispatcher() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Desktop size tools
|
||||||
|
|
||||||
|
Future<Size> _getSavedWindowSize() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
String? sizeString = prefs.getString(kAppWindowSize);
|
||||||
|
|
||||||
|
if (sizeString != null) {
|
||||||
|
List<String> parts = sizeString.split('x');
|
||||||
|
if (parts.length == 2) {
|
||||||
|
double? width = double.tryParse(parts[0]);
|
||||||
|
double? height = double.tryParse(parts[1]);
|
||||||
|
if (width != null && height != null) {
|
||||||
|
return Size(width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return const Size(1280, 720); // Default size
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _saveWindowSize() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final size = appWindow.size;
|
||||||
|
await prefs.setString(kAppWindowSize, '${size.width}x${size.height}');
|
||||||
|
}
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
if (!kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
|
if (!kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
|
||||||
|
final Size savedSize = await _getSavedWindowSize();
|
||||||
doWhenWindowReady(() {
|
doWhenWindowReady(() {
|
||||||
appWindow.minSize = Size(480, 640);
|
appWindow.minSize = Size(480, 640);
|
||||||
appWindow.size = Size(1280, 720);
|
appWindow.size = savedSize;
|
||||||
appWindow.alignment = Alignment.center;
|
appWindow.alignment = Alignment.center;
|
||||||
appWindow.show();
|
appWindow.show();
|
||||||
});
|
});
|
||||||
@@ -89,14 +120,16 @@ void main() async {
|
|||||||
await EasyLocalization.ensureInitialized();
|
await EasyLocalization.ensureInitialized();
|
||||||
|
|
||||||
if (!kIsWeb && !Platform.isLinux) {
|
if (!kIsWeb && !Platform.isLinux) {
|
||||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
await Firebase.initializeApp(
|
||||||
|
options: DefaultFirebaseOptions.currentPlatform);
|
||||||
}
|
}
|
||||||
|
|
||||||
GoRouter.optionURLReflectsImperativeAPIs = true;
|
GoRouter.optionURLReflectsImperativeAPIs = true;
|
||||||
usePathUrlStrategy();
|
usePathUrlStrategy();
|
||||||
|
|
||||||
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
||||||
Workmanager().initialize(appBackgroundDispatcher, isInDebugMode: kDebugMode);
|
Workmanager()
|
||||||
|
.initialize(appBackgroundDispatcher, isInDebugMode: kDebugMode);
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
Workmanager().registerPeriodicTask(
|
Workmanager().registerPeriodicTask(
|
||||||
"widget-update-random-post",
|
"widget-update-random-post",
|
||||||
@@ -109,7 +142,8 @@ void main() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!kIsWeb && Platform.isAndroid) {
|
if (!kIsWeb && Platform.isAndroid) {
|
||||||
final ImagePickerPlatform imagePickerImplementation = ImagePickerPlatform.instance;
|
final ImagePickerPlatform imagePickerImplementation =
|
||||||
|
ImagePickerPlatform.instance;
|
||||||
if (imagePickerImplementation is ImagePickerAndroid) {
|
if (imagePickerImplementation is ImagePickerAndroid) {
|
||||||
imagePickerImplementation.useAndroidPhotoPicker = true;
|
imagePickerImplementation.useAndroidPhotoPicker = true;
|
||||||
}
|
}
|
||||||
@@ -126,7 +160,12 @@ class SolianApp extends StatelessWidget {
|
|||||||
return ResponsiveBreakpoints.builder(
|
return ResponsiveBreakpoints.builder(
|
||||||
child: EasyLocalization(
|
child: EasyLocalization(
|
||||||
path: 'assets/translations',
|
path: 'assets/translations',
|
||||||
supportedLocales: [Locale('en', 'US'), Locale('zh', 'CN'), Locale('zh', 'TW'), Locale('zh', 'HK')],
|
supportedLocales: [
|
||||||
|
Locale('en', 'US'),
|
||||||
|
Locale('zh', 'CN'),
|
||||||
|
Locale('zh', 'TW'),
|
||||||
|
Locale('zh', 'HK')
|
||||||
|
],
|
||||||
fallbackLocale: Locale('en', 'US'),
|
fallbackLocale: Locale('en', 'US'),
|
||||||
useFallbackTranslations: true,
|
useFallbackTranslations: true,
|
||||||
assetLoader: JsonAssetLoader(),
|
assetLoader: JsonAssetLoader(),
|
||||||
@@ -201,7 +240,10 @@ class _AppDelegate extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
routerConfig: appRouter,
|
routerConfig: appRouter,
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
return _AppSplashScreen(key: const Key('global-splash-screen'), child: child!);
|
return _AppSplashScreen(
|
||||||
|
key: const Key('global-splash-screen'),
|
||||||
|
child: child!,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -218,6 +260,7 @@ class _AppSplashScreen extends StatefulWidget {
|
|||||||
|
|
||||||
class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
||||||
bool _isBusy = false;
|
bool _isBusy = false;
|
||||||
|
double _initPercentage = 0;
|
||||||
String _phaseText = 'appInitStarting';
|
String _phaseText = 'appInitStarting';
|
||||||
|
|
||||||
void _tryRequestRating() async {
|
void _tryRequestRating() async {
|
||||||
@@ -225,7 +268,8 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
if (prefs.containsKey('first_boot_time')) {
|
if (prefs.containsKey('first_boot_time')) {
|
||||||
final rawTime = prefs.getString('first_boot_time');
|
final rawTime = prefs.getString('first_boot_time');
|
||||||
final time = DateTime.tryParse(rawTime ?? '');
|
final time = DateTime.tryParse(rawTime ?? '');
|
||||||
if (time != null && time.isBefore(DateTime.now().subtract(const Duration(days: 3)))) {
|
if (time != null &&
|
||||||
|
time.isBefore(DateTime.now().subtract(const Duration(days: 3)))) {
|
||||||
final inAppReview = InAppReview.instance;
|
final inAppReview = InAppReview.instance;
|
||||||
if (prefs.getBool('rating_requested') == true) return;
|
if (prefs.getBool('rating_requested') == true) return;
|
||||||
if (await inAppReview.isAvailable()) {
|
if (await inAppReview.isAvailable()) {
|
||||||
@@ -246,17 +290,26 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
final info = await PackageInfo.fromPlatform();
|
final info = await PackageInfo.fromPlatform();
|
||||||
final localVersionString = '${info.version}+${info.buildNumber}';
|
final localVersionString = '${info.version}+${info.buildNumber}';
|
||||||
final resp = await Dio(
|
final resp = await Dio(
|
||||||
BaseOptions(sendTimeout: const Duration(seconds: 60), receiveTimeout: const Duration(seconds: 60)),
|
BaseOptions(
|
||||||
).get('https://api.github.com/repos/Solsynth/HyperNet.Surface/releases/latest');
|
sendTimeout: const Duration(seconds: 60),
|
||||||
|
receiveTimeout: const Duration(seconds: 60)),
|
||||||
|
).get(
|
||||||
|
'https://api.github.com/repos/Solsynth/HyperNet.Surface/releases/latest');
|
||||||
final remoteVersionString = resp.data?['tag_name'] ?? '0.0.0+0';
|
final remoteVersionString = resp.data?['tag_name'] ?? '0.0.0+0';
|
||||||
final remoteVersion = Version.parse(remoteVersionString.split('+').first);
|
final remoteVersion = Version.parse(remoteVersionString.split('+').first);
|
||||||
final localVersion = Version.parse(localVersionString.split('+').first);
|
final localVersion = Version.parse(localVersionString.split('+').first);
|
||||||
final remoteBuildNumber = int.tryParse(remoteVersionString.split('+').last) ?? 0;
|
final remoteBuildNumber =
|
||||||
final localBuildNumber = int.tryParse(localVersionString.split('+').last) ?? 0;
|
int.tryParse(remoteVersionString.split('+').last) ?? 0;
|
||||||
logging.info("[Update] Local: $localVersionString, Remote: $remoteVersionString");
|
final localBuildNumber =
|
||||||
if ((remoteVersion > localVersion || remoteBuildNumber > localBuildNumber) && mounted) {
|
int.tryParse(localVersionString.split('+').last) ?? 0;
|
||||||
|
logging.info(
|
||||||
|
"[Update] Local: $localVersionString, Remote: $remoteVersionString");
|
||||||
|
if ((remoteVersion > localVersion ||
|
||||||
|
remoteBuildNumber > localBuildNumber) &&
|
||||||
|
mounted) {
|
||||||
final config = context.read<ConfigProvider>();
|
final config = context.read<ConfigProvider>();
|
||||||
config.setUpdate(remoteVersionString, resp.data?['body'] ?? 'No changelog');
|
config.setUpdate(
|
||||||
|
remoteVersionString, resp.data?['body'] ?? 'No changelog');
|
||||||
logging.info("[Update] Update available: $remoteVersionString");
|
logging.info("[Update] Update available: $remoteVersionString");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -282,23 +335,27 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
// The Network initialization must be done after the HomeWidget initialization
|
// The Network initialization must be done after the HomeWidget initialization
|
||||||
// The Network initialization will save the server url to the HomeWidget
|
// The Network initialization will save the server url to the HomeWidget
|
||||||
// The Network initialization will also save initialize the Config, so it not need to be initialized again
|
// The Network initialization will also save initialize the Config, so it not need to be initialized again
|
||||||
|
_initPercentage = 0.1;
|
||||||
_setPhaseText('network');
|
_setPhaseText('network');
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
await sn.initializeUserAgent();
|
await sn.initializeUserAgent();
|
||||||
await sn.setConfigWithNative();
|
await sn.setConfigWithNative();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
_initPercentage = 0.2;
|
||||||
_setPhaseText('userdata');
|
_setPhaseText('userdata');
|
||||||
final ua = context.read<UserProvider>();
|
final ua = context.read<UserProvider>();
|
||||||
await ua.initialize();
|
await ua.initialize();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
_initPercentage = 0.3;
|
||||||
_setPhaseText('websocket');
|
_setPhaseText('websocket');
|
||||||
final ws = context.read<WebSocketProvider>();
|
final ws = context.read<WebSocketProvider>();
|
||||||
await ws.tryConnect();
|
await ws.tryConnect();
|
||||||
try {
|
try {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
_initPercentage = 0.9;
|
||||||
_setPhaseText('keyPair');
|
_setPhaseText('keyPair');
|
||||||
final kp = context.read<KeyPairProvider>();
|
final kp = context.read<KeyPairProvider>();
|
||||||
await kp.reloadActive();
|
kp.reloadActive();
|
||||||
kp.listen();
|
kp.listen();
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
if (ua.isAuthorized) {
|
if (ua.isAuthorized) {
|
||||||
@@ -307,8 +364,7 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
final notify = context.read<NotificationProvider>();
|
final notify = context.read<NotificationProvider>();
|
||||||
notify.listen();
|
notify.listen();
|
||||||
try {
|
try {
|
||||||
await notify.registerPushNotifications();
|
notify.registerPushNotifications();
|
||||||
} catch (_) {}
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
_setPhaseText('stickers');
|
_setPhaseText('stickers');
|
||||||
final sticker = context.read<SnStickerProvider>();
|
final sticker = context.read<SnStickerProvider>();
|
||||||
@@ -325,7 +381,10 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
_setPhaseText('chat');
|
_setPhaseText('chat');
|
||||||
final ct = context.read<ChatChannelProvider>();
|
final ct = context.read<ChatChannelProvider>();
|
||||||
await ct.refreshAvailableChannels();
|
await ct.refreshAvailableChannels();
|
||||||
|
_initPercentage = 1;
|
||||||
_setPhaseText('done');
|
_setPhaseText('done');
|
||||||
|
} catch (_) {}
|
||||||
|
_playIntro();
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
@@ -342,11 +401,39 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
// The quit key has been removed, and the logic of the quit key is moved to system menu bar activator.
|
// The quit key has been removed, and the logic of the quit key is moved to system menu bar activator.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _playIntro() async {
|
||||||
|
final cfg = context.read<ConfigProvider>();
|
||||||
|
if (!cfg.soundEffects) return;
|
||||||
|
|
||||||
|
final date = DateTime.now();
|
||||||
|
final player = AudioPlayer(playerId: 'launch-done-player');
|
||||||
|
await player.play(
|
||||||
|
(cfg.aprilFoolFeatures && date.month == 4 && date.day == 1)
|
||||||
|
? AssetSource('audio/sfx/launch-intro.mp3')
|
||||||
|
: AssetSource('audio/sfx/launch-done.mp3'),
|
||||||
|
volume: 0.8,
|
||||||
|
ctx: AudioContext(
|
||||||
|
android: AudioContextAndroid(
|
||||||
|
contentType: AndroidContentType.sonification,
|
||||||
|
usageType: AndroidUsageType.notificationEvent,
|
||||||
|
),
|
||||||
|
iOS: AudioContextIOS(category: AVAudioSessionCategory.ambient),
|
||||||
|
),
|
||||||
|
mode: PlayerMode.lowLatency,
|
||||||
|
);
|
||||||
|
player.onPlayerComplete.listen((_) {
|
||||||
|
player.dispose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
final Menu _appTrayMenu = Menu(
|
final Menu _appTrayMenu = Menu(
|
||||||
items: [
|
items: [
|
||||||
MenuItem(key: 'version_label', label: 'Solian', disabled: true),
|
MenuItem(key: 'version_label', label: 'Solian', disabled: true),
|
||||||
MenuItem.separator(),
|
MenuItem.separator(),
|
||||||
MenuItem.checkbox(checked: false, key: 'mute_notification', label: 'trayMenuMuteNotification'.tr()),
|
MenuItem.checkbox(
|
||||||
|
checked: false,
|
||||||
|
key: 'mute_notification',
|
||||||
|
label: 'trayMenuMuteNotification'.tr()),
|
||||||
MenuItem.separator(),
|
MenuItem.separator(),
|
||||||
MenuItem(key: 'window_show', label: 'trayMenuShow'.tr()),
|
MenuItem(key: 'window_show', label: 'trayMenuShow'.tr()),
|
||||||
MenuItem(key: 'exit', label: 'trayMenuExit'.tr()),
|
MenuItem(key: 'exit', label: 'trayMenuExit'.tr()),
|
||||||
@@ -356,7 +443,9 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
Future<void> _trayInitialization() async {
|
Future<void> _trayInitialization() async {
|
||||||
if (kIsWeb || Platform.isAndroid || Platform.isIOS) return;
|
if (kIsWeb || Platform.isAndroid || Platform.isIOS) return;
|
||||||
|
|
||||||
final icon = Platform.isWindows ? 'assets/icon/tray-icon.ico' : 'assets/icon/tray-icon.png';
|
final icon = Platform.isWindows
|
||||||
|
? 'assets/icon/tray-icon.ico'
|
||||||
|
: 'assets/icon/tray-icon.png';
|
||||||
final appVersion = await PackageInfo.fromPlatform();
|
final appVersion = await PackageInfo.fromPlatform();
|
||||||
|
|
||||||
trayManager.addListener(this);
|
trayManager.addListener(this);
|
||||||
@@ -374,7 +463,8 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
Future<void> _notifyInitialization() async {
|
Future<void> _notifyInitialization() async {
|
||||||
if (kIsWeb || Platform.isAndroid || Platform.isIOS) return;
|
if (kIsWeb || Platform.isAndroid || Platform.isIOS) return;
|
||||||
|
|
||||||
await localNotifier.setup(appName: 'Solian', shortcutPolicy: ShortcutPolicy.requireCreate);
|
await localNotifier.setup(
|
||||||
|
appName: 'Solian', shortcutPolicy: ShortcutPolicy.requireCreate);
|
||||||
}
|
}
|
||||||
|
|
||||||
AppLifecycleListener? _appLifecycleListener;
|
AppLifecycleListener? _appLifecycleListener;
|
||||||
@@ -385,9 +475,11 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
|
|
||||||
_isBusy = true;
|
_isBusy = true;
|
||||||
if (!kIsWeb && !(Platform.isIOS || Platform.isAndroid)) {
|
if (!kIsWeb && !(Platform.isIOS || Platform.isAndroid)) {
|
||||||
_appLifecycleListener = AppLifecycleListener(onExitRequested: _onExitRequested);
|
_appLifecycleListener =
|
||||||
|
AppLifecycleListener(onExitRequested: _onExitRequested);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
_trayInitialization();
|
_trayInitialization();
|
||||||
_hotkeyInitialization();
|
_hotkeyInitialization();
|
||||||
_notifyInitialization();
|
_notifyInitialization();
|
||||||
@@ -396,7 +488,13 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
_tryRequestRating();
|
_tryRequestRating();
|
||||||
_checkForUpdate();
|
_checkForUpdate();
|
||||||
setState(() => _isBusy = false);
|
setState(() => _isBusy = false);
|
||||||
|
}).catchError((err) {
|
||||||
|
logging.error('[Bootstrap] Unable to initialize app', err);
|
||||||
|
setState(() => _isBusy = false);
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
|
logging.error('[Bootstrap] Unable to initialize (pre-stage) app', err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<AppExitResponse> _onExitRequested() async {
|
Future<AppExitResponse> _onExitRequested() async {
|
||||||
@@ -405,6 +503,7 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _quitApp() {
|
void _quitApp() {
|
||||||
|
_saveWindowSize();
|
||||||
_appLifecycleListener?.dispose();
|
_appLifecycleListener?.dispose();
|
||||||
if (Platform.isWindows) {
|
if (Platform.isWindows) {
|
||||||
appWindow.close();
|
appWindow.close();
|
||||||
@@ -485,10 +584,206 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
return SizeChangedLayoutNotifier(
|
return SizeChangedLayoutNotifier(
|
||||||
child:
|
child: _isBusy
|
||||||
_isBusy
|
? _AppLoadingScreen(
|
||||||
? Material(
|
isBusy: _isBusy,
|
||||||
key: Key('app-splash-screen-$_isBusy'),
|
initPercentage: _initPercentage,
|
||||||
|
phaseText: _phaseText,
|
||||||
|
)
|
||||||
|
: widget.child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AppLoadingScreen extends StatelessWidget {
|
||||||
|
const _AppLoadingScreen({
|
||||||
|
required this.isBusy,
|
||||||
|
required this.initPercentage,
|
||||||
|
required this.phaseText,
|
||||||
|
});
|
||||||
|
|
||||||
|
final bool isBusy;
|
||||||
|
final double initPercentage;
|
||||||
|
final String phaseText;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (ResponsiveScaffold.getIsExpand(context)) {
|
||||||
|
return Material(
|
||||||
|
key: Key('app-splash-screen-$isBusy'),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage('assets/icon/kanban-1st.jpg'),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
opacity: 0.1,
|
||||||
|
),
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
backgroundBlendMode: BlendMode.darken,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TweenAnimationBuilder<double>(
|
||||||
|
tween: Tween(begin: 0, end: initPercentage),
|
||||||
|
duration: Duration(milliseconds: 300),
|
||||||
|
builder: (context, value, _) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('${(value * 100).toStringAsFixed(0)}%')
|
||||||
|
.padding(left: 32, bottom: 4),
|
||||||
|
LinearProgressIndicator(
|
||||||
|
value: value,
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
Radius.circular(0),
|
||||||
|
),
|
||||||
|
stopIndicatorColor: Colors.transparent,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
),
|
||||||
|
const Gap(24),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TweenAnimationBuilder<double>(
|
||||||
|
tween: Tween(begin: 0, end: initPercentage),
|
||||||
|
duration: Duration(milliseconds: 300),
|
||||||
|
builder: (context, value, _) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text('${(value * 100).toStringAsFixed(0)}%')
|
||||||
|
.padding(right: 32, bottom: 4),
|
||||||
|
Transform.flip(
|
||||||
|
flipX: true,
|
||||||
|
child: LinearProgressIndicator(
|
||||||
|
value: value,
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
Radius.circular(0),
|
||||||
|
),
|
||||||
|
stopIndicatorColor: Colors.transparent,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(24),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 240, minWidth: 160),
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.surface.withOpacity(0.85),
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).dividerColor,
|
||||||
|
width: 3,
|
||||||
|
),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'splashScreenServer',
|
||||||
|
style: GoogleFonts.notoSerifHk(height: 1, fontSize: 11),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
).tr().opacity(0.85),
|
||||||
|
Text(
|
||||||
|
'splashScreenServerName',
|
||||||
|
style: GoogleFonts.notoSerifHk(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
).tr().opacity(0.85),
|
||||||
|
Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
text: '#',
|
||||||
|
style: GoogleFonts.notoSerifHk(),
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: '0',
|
||||||
|
style: GoogleFonts.notoSerifHk(
|
||||||
|
fontSize: 80,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
).padding(vertical: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: MediaQuery.of(context).size.height * 0.2,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
phaseText,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
AnimateWidgetExtensions(Text(
|
||||||
|
'splashScreenCaption',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
).tr())
|
||||||
|
.animate(onPlay: (e) => e.repeat())
|
||||||
|
.fadeIn(duration: 500.ms, curve: Curves.easeOut)
|
||||||
|
.then()
|
||||||
|
.fadeOut(
|
||||||
|
duration: 500.ms,
|
||||||
|
delay: 1000.ms,
|
||||||
|
curve: Curves.easeIn,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: 8,
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
'assets/icon/icon.png',
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
).padding(all: 4),
|
||||||
|
const Gap(4),
|
||||||
|
Text('Solar Network').bold(),
|
||||||
|
Expanded(child: const SizedBox()),
|
||||||
|
AppVersionLabel(),
|
||||||
|
const Gap(12),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Material(
|
||||||
|
key: Key('app-splash-screen-$isBusy'),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
@@ -517,21 +812,20 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
Text('Solar Network').bold(),
|
Text('Solar Network').bold(),
|
||||||
AppVersionLabel(),
|
AppVersionLabel(),
|
||||||
Gap(8),
|
Gap(8),
|
||||||
Text(_phaseText, textAlign: TextAlign.center),
|
Text(phaseText, textAlign: TextAlign.center),
|
||||||
Gap(16),
|
Gap(16),
|
||||||
const LinearProgressIndicator(),
|
TweenAnimationBuilder<double>(
|
||||||
|
tween: Tween(begin: 0, end: initPercentage),
|
||||||
|
duration: Duration(milliseconds: 300),
|
||||||
|
builder: (context, value, _) =>
|
||||||
|
LinearProgressIndicator(value: value),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
|
||||||
: widget.child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:livekit_client/livekit_client.dart';
|
import 'package:livekit_client/livekit_client.dart';
|
||||||
|
import 'package:livekit_noise_filter/livekit_noise_filter.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
@@ -131,10 +132,14 @@ class ChatCallProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
void initRoom() {
|
void initRoom() {
|
||||||
initHardware();
|
initHardware();
|
||||||
|
final timeout = const Duration(seconds: 60);
|
||||||
_room = Room(
|
_room = Room(
|
||||||
roomOptions: const RoomOptions(
|
roomOptions: RoomOptions(
|
||||||
dynacast: true,
|
dynacast: true,
|
||||||
adaptiveStream: true,
|
adaptiveStream: true,
|
||||||
|
defaultAudioCaptureOptions: AudioCaptureOptions(
|
||||||
|
processor: LiveKitNoiseFilter(),
|
||||||
|
),
|
||||||
defaultAudioPublishOptions: AudioPublishOptions(
|
defaultAudioPublishOptions: AudioPublishOptions(
|
||||||
name: 'call_voice',
|
name: 'call_voice',
|
||||||
stream: 'call_stream',
|
stream: 'call_stream',
|
||||||
@@ -154,6 +159,16 @@ class ChatCallProvider extends ChangeNotifier {
|
|||||||
params: VideoParametersPresets.h1080_169,
|
params: VideoParametersPresets.h1080_169,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
connectOptions: ConnectOptions(
|
||||||
|
autoSubscribe: true,
|
||||||
|
timeouts: Timeouts(
|
||||||
|
connection: timeout,
|
||||||
|
debounce: timeout,
|
||||||
|
publish: timeout,
|
||||||
|
peerConnection: timeout,
|
||||||
|
iceRestart: timeout,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
_listener = _room.createListener();
|
_listener = _room.createListener();
|
||||||
WakelockPlus.enable();
|
WakelockPlus.enable();
|
||||||
|
@@ -13,7 +13,6 @@ const kNetworkServerStoreKey = 'app_server_url';
|
|||||||
const kAppbarTransparentStoreKey = 'app_bar_transparent';
|
const kAppbarTransparentStoreKey = 'app_bar_transparent';
|
||||||
const kAppBackgroundStoreKey = 'app_has_background';
|
const kAppBackgroundStoreKey = 'app_has_background';
|
||||||
const kAppColorSchemeStoreKey = 'app_color_scheme';
|
const kAppColorSchemeStoreKey = 'app_color_scheme';
|
||||||
const kAppDrawerPreferCollapse = 'app_drawer_prefer_collapse';
|
|
||||||
const kAppNotifyWithHaptic = 'app_notify_with_haptic';
|
const kAppNotifyWithHaptic = 'app_notify_with_haptic';
|
||||||
const kAppExpandPostLink = 'app_expand_post_link';
|
const kAppExpandPostLink = 'app_expand_post_link';
|
||||||
const kAppExpandChatLink = 'app_expand_chat_link';
|
const kAppExpandChatLink = 'app_expand_chat_link';
|
||||||
@@ -22,6 +21,9 @@ const kAppCustomFonts = 'app_custom_fonts';
|
|||||||
const kAppMixedFeed = 'app_mixed_feed';
|
const kAppMixedFeed = 'app_mixed_feed';
|
||||||
const kAppAutoTranslate = 'app_auto_translate';
|
const kAppAutoTranslate = 'app_auto_translate';
|
||||||
const kAppHideBottomNav = 'app_hide_bottom_nav';
|
const kAppHideBottomNav = 'app_hide_bottom_nav';
|
||||||
|
const kAppSoundEffects = 'app_sound_effects';
|
||||||
|
const kAppAprilFoolFeatures = 'app_april_fool_features';
|
||||||
|
const kAppWindowSize = 'app_window_size';
|
||||||
|
|
||||||
const Map<String, FilterQuality> kImageQualityLevel = {
|
const Map<String, FilterQuality> kImageQualityLevel = {
|
||||||
'settingsImageQualityLowest': FilterQuality.none,
|
'settingsImageQualityLowest': FilterQuality.none,
|
||||||
@@ -44,27 +46,17 @@ class ConfigProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool drawerIsCollapsed = false;
|
bool drawerIsCollapsed = false;
|
||||||
bool drawerIsExpanded = false;
|
|
||||||
|
|
||||||
void calcDrawerSize(BuildContext context, {bool withMediaQuery = false}) {
|
void calcDrawerSize(BuildContext context, {bool withMediaQuery = false}) {
|
||||||
bool newDrawerIsCollapsed = false;
|
bool newDrawerIsCollapsed = false;
|
||||||
bool newDrawerIsExpanded = false;
|
|
||||||
if (withMediaQuery) {
|
if (withMediaQuery) {
|
||||||
newDrawerIsCollapsed = MediaQuery.of(context).size.width < 600;
|
newDrawerIsCollapsed = MediaQuery.of(context).size.width < 600;
|
||||||
newDrawerIsExpanded = MediaQuery.of(context).size.width >= 601;
|
|
||||||
} else {
|
} else {
|
||||||
final rpb = ResponsiveBreakpoints.of(context);
|
final rpb = ResponsiveBreakpoints.of(context);
|
||||||
newDrawerIsCollapsed = rpb.smallerOrEqualTo(MOBILE);
|
newDrawerIsCollapsed = rpb.smallerOrEqualTo(MOBILE);
|
||||||
newDrawerIsExpanded = rpb.largerThan(TABLET)
|
|
||||||
? (prefs.getBool(kAppDrawerPreferCollapse) ?? false)
|
|
||||||
? false
|
|
||||||
: true
|
|
||||||
: false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newDrawerIsExpanded != drawerIsExpanded ||
|
if (newDrawerIsCollapsed != drawerIsCollapsed) {
|
||||||
newDrawerIsCollapsed != drawerIsCollapsed) {
|
|
||||||
drawerIsExpanded = newDrawerIsExpanded;
|
|
||||||
drawerIsCollapsed = newDrawerIsCollapsed;
|
drawerIsCollapsed = newDrawerIsCollapsed;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
@@ -96,6 +88,24 @@ class ConfigProvider extends ChangeNotifier {
|
|||||||
return prefs.getBool(kAppHideBottomNav) ?? false;
|
return prefs.getBool(kAppHideBottomNav) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get aprilFoolFeatures {
|
||||||
|
return prefs.getBool(kAppAprilFoolFeatures) ?? true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get soundEffects {
|
||||||
|
return prefs.getBool(kAppSoundEffects) ?? true;
|
||||||
|
}
|
||||||
|
|
||||||
|
set soundEffects(bool value) {
|
||||||
|
prefs.setBool(kAppSoundEffects, value);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
set aprilFoolFeatures(bool value) {
|
||||||
|
prefs.setBool(kAppAprilFoolFeatures, value);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
set hideBottomNav(bool value) {
|
set hideBottomNav(bool value) {
|
||||||
prefs.setBool(kAppHideBottomNav, value);
|
prefs.setBool(kAppHideBottomNav, value);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
@@ -4,7 +4,20 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:surface/types/realm.dart';
|
|
||||||
|
class AppNavListItem {
|
||||||
|
final String title;
|
||||||
|
final String subtitle;
|
||||||
|
final String screen;
|
||||||
|
final IconData icon;
|
||||||
|
|
||||||
|
const AppNavListItem({
|
||||||
|
required this.title,
|
||||||
|
required this.subtitle,
|
||||||
|
required this.screen,
|
||||||
|
required this.icon,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class AppNavDestination {
|
class AppNavDestination {
|
||||||
final String label;
|
final String label;
|
||||||
@@ -46,11 +59,6 @@ class NavigationProvider extends ChangeNotifier {
|
|||||||
screen: 'chat',
|
screen: 'chat',
|
||||||
label: 'screenChat',
|
label: 'screenChat',
|
||||||
),
|
),
|
||||||
AppNavDestination(
|
|
||||||
icon: Icon(Symbols.account_circle, weight: 400, opticalSize: 20),
|
|
||||||
screen: 'account',
|
|
||||||
label: 'screenAccount',
|
|
||||||
),
|
|
||||||
AppNavDestination(
|
AppNavDestination(
|
||||||
icon: Icon(Symbols.group, weight: 400, opticalSize: 20),
|
icon: Icon(Symbols.group, weight: 400, opticalSize: 20),
|
||||||
screen: 'realm',
|
screen: 'realm',
|
||||||
@@ -61,6 +69,11 @@ class NavigationProvider extends ChangeNotifier {
|
|||||||
screen: 'news',
|
screen: 'news',
|
||||||
label: 'screenNews',
|
label: 'screenNews',
|
||||||
),
|
),
|
||||||
|
AppNavDestination(
|
||||||
|
icon: Icon(Symbols.settings, weight: 400, opticalSize: 20),
|
||||||
|
screen: 'settings',
|
||||||
|
label: 'screenSettings',
|
||||||
|
),
|
||||||
];
|
];
|
||||||
static const List<String> kDefaultPinnedDestination = [
|
static const List<String> kDefaultPinnedDestination = [
|
||||||
'home',
|
'home',
|
||||||
@@ -121,11 +134,4 @@ class NavigationProvider extends ChangeNotifier {
|
|||||||
_currentIndex = idx;
|
_currentIndex = idx;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
SnRealm? focusedRealm;
|
|
||||||
|
|
||||||
void setFocusedRealm(SnRealm? realm) {
|
|
||||||
focusedRealm = realm;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:audioplayers/audioplayers.dart';
|
||||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@@ -22,6 +23,8 @@ class NotificationProvider extends ChangeNotifier {
|
|||||||
late final WebSocketProvider _ws;
|
late final WebSocketProvider _ws;
|
||||||
late final ConfigProvider _cfg;
|
late final ConfigProvider _cfg;
|
||||||
|
|
||||||
|
final AudioPlayer _notifySoundPlayer = AudioPlayer(playerId: 'notify-sound');
|
||||||
|
|
||||||
NotificationProvider(BuildContext context) {
|
NotificationProvider(BuildContext context) {
|
||||||
_sn = context.read<SnNetworkProvider>();
|
_sn = context.read<SnNetworkProvider>();
|
||||||
_ua = context.read<UserProvider>();
|
_ua = context.read<UserProvider>();
|
||||||
@@ -48,11 +51,13 @@ class NotificationProvider extends ChangeNotifier {
|
|||||||
var deviceUuid = await FlutterUdid.consistentUdid;
|
var deviceUuid = await FlutterUdid.consistentUdid;
|
||||||
|
|
||||||
if (deviceUuid.isEmpty) {
|
if (deviceUuid.isEmpty) {
|
||||||
logging.warning('[Push Notification] Unable to active push notifications, couldn\'t get device uuid');
|
logging.warning(
|
||||||
|
'[Push Notification] Unable to active push notifications, couldn\'t get device uuid');
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
logging.info('[Push Notification] Device UUID is $deviceUuid');
|
logging.info('[Push Notification] Device UUID is $deviceUuid');
|
||||||
logging.info('[Push Notification] Registering device push notifications...');
|
logging
|
||||||
|
.info('[Push Notification] Registering device push notifications...');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Platform.isIOS || Platform.isMacOS) {
|
if (Platform.isIOS || Platform.isMacOS) {
|
||||||
@@ -67,10 +72,15 @@ class NotificationProvider extends ChangeNotifier {
|
|||||||
try {
|
try {
|
||||||
await _sn.client.post(
|
await _sn.client.post(
|
||||||
'/cgi/id/notifications/subscription',
|
'/cgi/id/notifications/subscription',
|
||||||
data: {'provider': provider, 'device_token': token, 'device_id': deviceUuid},
|
data: {
|
||||||
|
'provider': provider,
|
||||||
|
'device_token': token,
|
||||||
|
'device_id': deviceUuid
|
||||||
|
},
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logging.error('[Push Notification] Unable to register push notifications: $err');
|
logging.error(
|
||||||
|
'[Push Notification] Unable to register push notifications: $err');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +99,27 @@ class NotificationProvider extends ChangeNotifier {
|
|||||||
final doHaptic = _cfg.prefs.getBool(kAppNotifyWithHaptic) ?? true;
|
final doHaptic = _cfg.prefs.getBool(kAppNotifyWithHaptic) ?? true;
|
||||||
if (doHaptic) HapticFeedback.mediumImpact();
|
if (doHaptic) HapticFeedback.mediumImpact();
|
||||||
|
|
||||||
if (notification.topic == 'messaging.message' && skippableNotifyChannel != null) {
|
// April fool notification sfx
|
||||||
|
if (_cfg.prefs.getBool(kAppAprilFoolFeatures) ?? true) {
|
||||||
|
final now = DateTime.now();
|
||||||
|
if (now.day == 1 && now.month == 4) {
|
||||||
|
_notifySoundPlayer.play(
|
||||||
|
AssetSource('audio/notify/metal-pipe.mp3'),
|
||||||
|
volume: 0.6,
|
||||||
|
ctx: AudioContext(
|
||||||
|
android: AudioContextAndroid(
|
||||||
|
contentType: AndroidContentType.sonification,
|
||||||
|
usageType: AndroidUsageType.notificationEvent,
|
||||||
|
),
|
||||||
|
iOS: AudioContextIOS(category: AVAudioSessionCategory.ambient),
|
||||||
|
),
|
||||||
|
mode: PlayerMode.lowLatency,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notification.topic == 'messaging.message' &&
|
||||||
|
skippableNotifyChannel != null) {
|
||||||
if (notification.metadata['channel_id'] != null &&
|
if (notification.metadata['channel_id'] != null &&
|
||||||
notification.metadata['channel_id'] == skippableNotifyChannel) {
|
notification.metadata['channel_id'] == skippableNotifyChannel) {
|
||||||
return;
|
return;
|
||||||
|
@@ -1,144 +1,31 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:surface/providers/sn_attachment.dart';
|
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/providers/sn_realm.dart';
|
|
||||||
import 'package:surface/providers/user_directory.dart';
|
|
||||||
import 'package:surface/types/poll.dart';
|
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
import 'package:surface/types/realm.dart';
|
|
||||||
|
|
||||||
class SnPostContentProvider {
|
class SnPostContentProvider {
|
||||||
late final SnNetworkProvider _sn;
|
late final SnNetworkProvider _sn;
|
||||||
late final UserDirectoryProvider _ud;
|
|
||||||
late final SnAttachmentProvider _attach;
|
|
||||||
late final SnRealmProvider _realm;
|
|
||||||
|
|
||||||
SnPostContentProvider(BuildContext context) {
|
SnPostContentProvider(BuildContext context) {
|
||||||
_sn = context.read<SnNetworkProvider>();
|
_sn = context.read<SnNetworkProvider>();
|
||||||
_ud = context.read<UserDirectoryProvider>();
|
|
||||||
_attach = context.read<SnAttachmentProvider>();
|
|
||||||
_realm = context.read<SnRealmProvider>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<SnPoll> _fetchPoll(int id) async {
|
|
||||||
final resp = await _sn.client.get('/cgi/co/polls/$id');
|
|
||||||
return SnPoll.fromJson(resp.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SnPost>> _preloadRelatedDataInBatch(List<SnPost> out) async {
|
Future<List<SnPost>> _preloadRelatedDataInBatch(List<SnPost> out) async {
|
||||||
Set<String> rids = {};
|
|
||||||
Set<int> uids = {};
|
|
||||||
for (var i = 0; i < out.length; i++) {
|
|
||||||
rids.addAll(out[i].body['attachments']?.cast<String>() ?? []);
|
|
||||||
if (out[i].body['thumbnail'] != null) {
|
|
||||||
rids.add(out[i].body['thumbnail']);
|
|
||||||
}
|
|
||||||
if (out[i].body['video'] != null) {
|
|
||||||
rids.add(out[i].body['video']);
|
|
||||||
}
|
|
||||||
if (out[i].repostTo != null) {
|
|
||||||
out[i] = out[i].copyWith(
|
|
||||||
repostTo: await _preloadRelatedDataSingle(out[i].repostTo!),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (out[i].publisher.type == 0) {
|
|
||||||
uids.add(out[i].publisher.accountId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final attachments = await _attach.getMultiple(rids.toList());
|
|
||||||
for (var i = 0; i < out.length; i++) {
|
|
||||||
SnPoll? poll;
|
|
||||||
SnRealm? realm;
|
|
||||||
if (out[i].pollId != null) {
|
|
||||||
poll = await _fetchPoll(out[i].pollId!);
|
|
||||||
}
|
|
||||||
if (out[i].realmId != null) {
|
|
||||||
realm = await _realm.getRealm(out[i].realmId!);
|
|
||||||
}
|
|
||||||
|
|
||||||
out[i] = out[i].copyWith(
|
|
||||||
preload: SnPostPreload(
|
|
||||||
thumbnail: attachments
|
|
||||||
.where((ele) => ele?.rid == out[i].body['thumbnail'])
|
|
||||||
.firstOrNull,
|
|
||||||
attachments: attachments
|
|
||||||
.where((ele) =>
|
|
||||||
out[i].body['attachments']?.contains(ele?.rid) ?? false)
|
|
||||||
.toList(),
|
|
||||||
video: attachments
|
|
||||||
.where((ele) => ele?.rid == out[i].body['video'])
|
|
||||||
.firstOrNull,
|
|
||||||
poll: poll,
|
|
||||||
realm: realm,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
uids.addAll(
|
|
||||||
attachments.where((ele) => ele != null).map((ele) => ele!.accountId));
|
|
||||||
await _ud.listAccount(uids);
|
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SnPost> _preloadRelatedDataSingle(SnPost out) async {
|
Future<SnPost> _preloadRelatedDataSingle(SnPost out) async {
|
||||||
Set<String> rids = {};
|
|
||||||
Set<int> uids = {};
|
|
||||||
rids.addAll(out.body['attachments']?.cast<String>() ?? []);
|
|
||||||
if (out.body['thumbnail'] != null) {
|
|
||||||
rids.add(out.body['thumbnail']);
|
|
||||||
}
|
|
||||||
if (out.body['video'] != null) {
|
|
||||||
rids.add(out.body['video']);
|
|
||||||
}
|
|
||||||
if (out.repostTo != null) {
|
|
||||||
out = out.copyWith(
|
|
||||||
repostTo: await _preloadRelatedDataSingle(out.repostTo!),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (out.publisher.type == 0) {
|
|
||||||
uids.add(out.publisher.accountId);
|
|
||||||
}
|
|
||||||
|
|
||||||
final attachments = await _attach.getMultiple(rids.toList());
|
|
||||||
|
|
||||||
SnPoll? poll;
|
|
||||||
SnRealm? realm;
|
|
||||||
if (out.pollId != null) {
|
|
||||||
poll = await _fetchPoll(out.pollId!);
|
|
||||||
}
|
|
||||||
if (out.realmId != null) {
|
|
||||||
realm = await _realm.getRealm(out.realmId!);
|
|
||||||
}
|
|
||||||
|
|
||||||
out = out.copyWith(
|
|
||||||
preload: SnPostPreload(
|
|
||||||
thumbnail: attachments
|
|
||||||
.where((ele) => ele?.rid == out.body['thumbnail'])
|
|
||||||
.firstOrNull,
|
|
||||||
attachments: attachments
|
|
||||||
.where(
|
|
||||||
(ele) => out.body['attachments']?.contains(ele?.rid) ?? false)
|
|
||||||
.toList(),
|
|
||||||
video: attachments
|
|
||||||
.where((ele) => ele?.rid == out.body['video'])
|
|
||||||
.firstOrNull,
|
|
||||||
poll: poll,
|
|
||||||
realm: realm,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
uids.addAll(
|
|
||||||
attachments.where((ele) => ele != null).map((ele) => ele!.accountId));
|
|
||||||
await _ud.listAccount(uids);
|
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SnPost>> listRecommendations() async {
|
Future<List<SnPost>> listRecommendations() async {
|
||||||
final resp = await _sn.client.get('/cgi/co/recommendations');
|
final resp = await _sn.client.get(
|
||||||
|
'/cgi/co/recommendations',
|
||||||
|
options: Options(headers: {
|
||||||
|
'X-API-Version': '2',
|
||||||
|
}),
|
||||||
|
);
|
||||||
final out = _preloadRelatedDataInBatch(
|
final out = _preloadRelatedDataInBatch(
|
||||||
List.from(resp.data.map((ele) => SnPost.fromJson(ele))),
|
List.from(resp.data.map((ele) => SnPost.fromJson(ele))),
|
||||||
);
|
);
|
||||||
@@ -202,6 +89,9 @@ class SnPostContentProvider {
|
|||||||
if (realm != null) 'realm': realm,
|
if (realm != null) 'realm': realm,
|
||||||
if (channel != null) 'channel': channel,
|
if (channel != null) 'channel': channel,
|
||||||
},
|
},
|
||||||
|
options: Options(headers: {
|
||||||
|
'X-API-Version': '2',
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
final List<SnPost> out = await _preloadRelatedDataInBatch(
|
final List<SnPost> out = await _preloadRelatedDataInBatch(
|
||||||
List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []),
|
List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []),
|
||||||
@@ -215,11 +105,16 @@ class SnPostContentProvider {
|
|||||||
int take = 10,
|
int take = 10,
|
||||||
int offset = 0,
|
int offset = 0,
|
||||||
}) async {
|
}) async {
|
||||||
final resp = await _sn.client
|
final resp = await _sn.client.get(
|
||||||
.get('/cgi/co/posts/$parentId/replies', queryParameters: {
|
'/cgi/co/posts/$parentId/replies',
|
||||||
|
queryParameters: {
|
||||||
'take': take,
|
'take': take,
|
||||||
'offset': offset,
|
'offset': offset,
|
||||||
});
|
},
|
||||||
|
options: Options(headers: {
|
||||||
|
'X-API-Version': '2',
|
||||||
|
}),
|
||||||
|
);
|
||||||
final List<SnPost> out = await _preloadRelatedDataInBatch(
|
final List<SnPost> out = await _preloadRelatedDataInBatch(
|
||||||
List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []),
|
List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []),
|
||||||
);
|
);
|
||||||
@@ -234,13 +129,20 @@ class SnPostContentProvider {
|
|||||||
Iterable<String>? tags,
|
Iterable<String>? tags,
|
||||||
Iterable<String>? categories,
|
Iterable<String>? categories,
|
||||||
}) async {
|
}) async {
|
||||||
final resp = await _sn.client.get('/cgi/co/posts/search', queryParameters: {
|
final resp = await _sn.client.get(
|
||||||
|
'/cgi/co/posts/search',
|
||||||
|
queryParameters: {
|
||||||
'take': take,
|
'take': take,
|
||||||
'offset': offset,
|
'offset': offset,
|
||||||
'probe': searchTerm,
|
'probe': searchTerm,
|
||||||
if (tags?.isNotEmpty ?? false) 'tags': tags!.join(','),
|
if (tags?.isNotEmpty ?? false) 'tags': tags!.join(','),
|
||||||
if (categories?.isNotEmpty ?? false) 'categories': categories!.join(','),
|
if (categories?.isNotEmpty ?? false)
|
||||||
});
|
'categories': categories!.join(','),
|
||||||
|
},
|
||||||
|
options: Options(headers: {
|
||||||
|
'X-API-Version': '2',
|
||||||
|
}),
|
||||||
|
);
|
||||||
final List<SnPost> out = await _preloadRelatedDataInBatch(
|
final List<SnPost> out = await _preloadRelatedDataInBatch(
|
||||||
List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []),
|
List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []),
|
||||||
);
|
);
|
||||||
@@ -249,7 +151,12 @@ class SnPostContentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<SnPost> getPost(dynamic id) async {
|
Future<SnPost> getPost(dynamic id) async {
|
||||||
final resp = await _sn.client.get('/cgi/co/posts/$id');
|
final resp = await _sn.client.get(
|
||||||
|
'/cgi/co/posts/$id',
|
||||||
|
options: Options(headers: {
|
||||||
|
'X-API-Version': '2',
|
||||||
|
}),
|
||||||
|
);
|
||||||
final out = _preloadRelatedDataSingle(
|
final out = _preloadRelatedDataSingle(
|
||||||
SnPost.fromJson(resp.data),
|
SnPost.fromJson(resp.data),
|
||||||
);
|
);
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import 'package:animations/animations.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:surface/screens/abuse_report.dart';
|
import 'package:surface/screens/abuse_report.dart';
|
||||||
import 'package:surface/screens/account.dart';
|
import 'package:surface/screens/account.dart';
|
||||||
import 'package:surface/screens/account/account_settings.dart';
|
import 'package:surface/screens/account/punishments.dart';
|
||||||
|
import 'package:surface/screens/account/settings.dart';
|
||||||
import 'package:surface/screens/account/action_events.dart';
|
import 'package:surface/screens/account/action_events.dart';
|
||||||
import 'package:surface/screens/account/badges.dart';
|
import 'package:surface/screens/account/badges.dart';
|
||||||
import 'package:surface/screens/account/contact_methods.dart';
|
import 'package:surface/screens/account/contact_methods.dart';
|
||||||
@@ -53,16 +53,6 @@ import 'package:surface/types/post.dart';
|
|||||||
import 'package:surface/widgets/about.dart';
|
import 'package:surface/widgets/about.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||||
|
|
||||||
Widget _fadeThroughTransition(BuildContext context, Animation<double> animation,
|
|
||||||
Animation<double> secondaryAnimation, Widget child) {
|
|
||||||
return FadeThroughTransition(
|
|
||||||
animation: animation,
|
|
||||||
secondaryAnimation: secondaryAnimation,
|
|
||||||
fillColor: Colors.transparent,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final _appRoutes = [
|
final _appRoutes = [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/',
|
path: '/',
|
||||||
@@ -71,8 +61,8 @@ final _appRoutes = [
|
|||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/posts',
|
path: '/posts',
|
||||||
name: 'explore',
|
name: 'posts',
|
||||||
builder: (context, state) => const ExploreScreen(),
|
builder: (_, __) => const SizedBox.shrink(),
|
||||||
routes: [
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/draft',
|
path: '/draft',
|
||||||
@@ -110,27 +100,57 @@ final _appRoutes = [
|
|||||||
state.uri.queryParameters['categories']?.split(','),
|
state.uri.queryParameters['categories']?.split(','),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
ShellRoute(
|
||||||
|
builder: (context, state, child) => ResponsiveScaffold(
|
||||||
|
asideFlex: 2,
|
||||||
|
contentFlex: 3,
|
||||||
|
aside: const ExploreScreen(),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: '/explore',
|
||||||
|
name: 'explore',
|
||||||
|
builder: (context, state) => const ResponsiveScaffoldLanding(
|
||||||
|
child: ExploreScreen(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/posts/:slug',
|
||||||
|
name: 'postDetail',
|
||||||
|
builder: (context, state) => PostDetailScreen(
|
||||||
|
key: ValueKey(state.pathParameters['slug']!),
|
||||||
|
slug: state.pathParameters['slug']!,
|
||||||
|
preload: state.extra as SnPost?,
|
||||||
|
),
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/publishers/:name',
|
path: '/publishers/:name',
|
||||||
name: 'postPublisher',
|
name: 'postPublisher',
|
||||||
builder: (context, state) =>
|
builder: (context, state) =>
|
||||||
PostPublisherScreen(name: state.pathParameters['name']!),
|
PostPublisherScreen(name: state.pathParameters['name']!),
|
||||||
),
|
),
|
||||||
GoRoute(
|
|
||||||
path: '/:slug',
|
|
||||||
name: 'postDetail',
|
|
||||||
builder: (context, state) => PostDetailScreen(
|
|
||||||
slug: state.pathParameters['slug']!,
|
|
||||||
preload: state.extra as SnPost?,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
ShellRoute(
|
||||||
|
builder: (context, state, child) => ResponsiveScaffold(
|
||||||
|
aside: const AccountScreen(),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/account',
|
path: '/account',
|
||||||
name: 'account',
|
name: 'account',
|
||||||
builder: (context, state) => const AccountScreen(),
|
builder: (context, state) =>
|
||||||
|
const ResponsiveScaffoldLanding(child: AccountScreen()),
|
||||||
routes: [
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: '/punishments',
|
||||||
|
name: 'accountPunishments',
|
||||||
|
builder: (context, state) => const PunishmentsScreen(),
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/programs',
|
path: '/programs',
|
||||||
name: 'accountProgram',
|
name: 'accountProgram',
|
||||||
@@ -210,24 +230,35 @@ final _appRoutes = [
|
|||||||
name: state.pathParameters['name']!,
|
name: state.pathParameters['name']!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/profile/:name',
|
path: '/accounts/:name',
|
||||||
name: 'accountProfilePage',
|
name: 'accountProfilePage',
|
||||||
pageBuilder: (context, state) => NoTransitionPage(
|
pageBuilder: (context, state) => NoTransitionPage(
|
||||||
child: UserScreen(name: state.pathParameters['name']!),
|
child: UserScreen(name: state.pathParameters['name']!),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
ShellRoute(
|
||||||
),
|
builder: (context, state, child) =>
|
||||||
|
ResponsiveScaffold(aside: const ChatScreen(), child: child),
|
||||||
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/chat',
|
path: '/chat',
|
||||||
name: 'chat',
|
name: 'chat',
|
||||||
builder: (context, state) => const ChatScreen(),
|
builder: (context, state) => const ResponsiveScaffoldLanding(
|
||||||
|
child: ChatScreen(),
|
||||||
|
),
|
||||||
routes: [
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/:scope/:alias',
|
path: '/:scope/:alias',
|
||||||
name: 'chatRoom',
|
name: 'chatRoom',
|
||||||
builder: (context, state) => ChatRoomScreen(
|
builder: (context, state) => ChatRoomScreen(
|
||||||
|
key: ValueKey(
|
||||||
|
'${state.pathParameters['scope']!}:${state.pathParameters['alias']!}',
|
||||||
|
),
|
||||||
scope: state.pathParameters['scope']!,
|
scope: state.pathParameters['scope']!,
|
||||||
alias: state.pathParameters['alias']!,
|
alias: state.pathParameters['alias']!,
|
||||||
extra: state.extra as ChatRoomScreenExtra?,
|
extra: state.extra as ChatRoomScreenExtra?,
|
||||||
@@ -258,13 +289,12 @@ final _appRoutes = [
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/realm',
|
path: '/realm',
|
||||||
name: 'realm',
|
name: 'realm',
|
||||||
pageBuilder: (context, state) => CustomTransitionPage(
|
builder: (context, state) => const RealmScreen(),
|
||||||
transitionsBuilder: _fadeThroughTransition,
|
|
||||||
child: const RealmScreen(),
|
|
||||||
),
|
|
||||||
routes: [
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/:alias/community',
|
path: '/:alias/community',
|
||||||
|
@@ -8,6 +8,7 @@ import 'package:material_symbols_icons/symbols.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/database.dart';
|
import 'package:surface/providers/database.dart';
|
||||||
|
import 'package:surface/providers/navigation.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/providers/websocket.dart';
|
import 'package:surface/providers/websocket.dart';
|
||||||
@@ -22,12 +23,94 @@ import 'package:surface/widgets/universal_image.dart';
|
|||||||
class AccountScreen extends StatelessWidget {
|
class AccountScreen extends StatelessWidget {
|
||||||
const AccountScreen({super.key});
|
const AccountScreen({super.key});
|
||||||
|
|
||||||
|
static const List<AppNavListItem> kNavList = [
|
||||||
|
AppNavListItem(
|
||||||
|
title: "accountPublishers",
|
||||||
|
subtitle: "accountPublishersSubtitle",
|
||||||
|
screen: "accountPublishers",
|
||||||
|
icon: Symbols.face,
|
||||||
|
),
|
||||||
|
AppNavListItem(
|
||||||
|
title: "accountProgram",
|
||||||
|
subtitle: "accountProgramDescription",
|
||||||
|
screen: "accountProgram",
|
||||||
|
icon: Symbols.communities,
|
||||||
|
),
|
||||||
|
AppNavListItem(
|
||||||
|
title: "friends",
|
||||||
|
subtitle: "friendsDescription",
|
||||||
|
screen: "friend",
|
||||||
|
icon: Symbols.person,
|
||||||
|
),
|
||||||
|
AppNavListItem(
|
||||||
|
title: "album",
|
||||||
|
subtitle: "albumDescription",
|
||||||
|
screen: "album",
|
||||||
|
icon: Symbols.photo_library,
|
||||||
|
),
|
||||||
|
AppNavListItem(
|
||||||
|
title: "stickers",
|
||||||
|
subtitle: "stickersDescription",
|
||||||
|
screen: "stickers",
|
||||||
|
icon: Symbols.emoji_emotions,
|
||||||
|
),
|
||||||
|
AppNavListItem(
|
||||||
|
title: "accountWallet",
|
||||||
|
subtitle: "accountWalletSubtitle",
|
||||||
|
screen: "accountWallet",
|
||||||
|
icon: Symbols.wallet,
|
||||||
|
),
|
||||||
|
AppNavListItem(
|
||||||
|
title: "accountBadges",
|
||||||
|
subtitle: "accountBadgesDescription",
|
||||||
|
screen: "accountBadges",
|
||||||
|
icon: Symbols.award_star,
|
||||||
|
),
|
||||||
|
AppNavListItem(
|
||||||
|
title: "accountKeyPairs",
|
||||||
|
subtitle: "accountKeyPairsDescription",
|
||||||
|
screen: "accountKeyPairs",
|
||||||
|
icon: Symbols.key,
|
||||||
|
),
|
||||||
|
AppNavListItem(
|
||||||
|
title: "accountPunishments",
|
||||||
|
subtitle: "accountPunishmentsDescription",
|
||||||
|
screen: "accountPunishments",
|
||||||
|
icon: Symbols.credit_score,
|
||||||
|
),
|
||||||
|
AppNavListItem(
|
||||||
|
title: "accountActionEvent",
|
||||||
|
subtitle: "accountActionEventDescription",
|
||||||
|
screen: "accountActionEvents",
|
||||||
|
icon: Symbols.history,
|
||||||
|
),
|
||||||
|
AppNavListItem(
|
||||||
|
title: "accountAuthTickets",
|
||||||
|
subtitle: "accountAuthTicketsDescription",
|
||||||
|
screen: "accountAuthTickets",
|
||||||
|
icon: Symbols.confirmation_number,
|
||||||
|
),
|
||||||
|
AppNavListItem(
|
||||||
|
title: "accountSettings",
|
||||||
|
subtitle: "accountSettingsSubtitle",
|
||||||
|
screen: "accountSettings",
|
||||||
|
icon: Symbols.manage_accounts,
|
||||||
|
),
|
||||||
|
AppNavListItem(
|
||||||
|
title: "abuseReport",
|
||||||
|
subtitle: "abuseReportActionDescription",
|
||||||
|
screen: "abuseReport",
|
||||||
|
icon: Symbols.flag,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ua = context.watch<UserProvider>();
|
final ua = context.watch<UserProvider>();
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: AutoAppBarLeading(),
|
leading: AutoAppBarLeading(),
|
||||||
title: Text("screenAccount").tr(),
|
title: Text("screenAccount").tr(),
|
||||||
@@ -59,15 +142,6 @@ class AccountScreen extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Symbols.settings, fill: 1),
|
|
||||||
onPressed: () {
|
|
||||||
GoRouter.of(context).pushNamed('settings');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const Gap(8),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: ua.isAuthorized
|
child: ua.isAuthorized
|
||||||
@@ -106,7 +180,18 @@ class _AuthorizedAccountScreen extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
AccountImage(content: ua.user!.avatar, radius: 28),
|
GestureDetector(
|
||||||
|
child: AccountImage(
|
||||||
|
content: ua.user!.avatar,
|
||||||
|
radius: 28,
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context)
|
||||||
|
.pushNamed('accountProfilePage', pathParameters: {
|
||||||
|
'name': ua.user!.name,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
_AccountStatusWidget(account: ua.user!),
|
_AccountStatusWidget(account: ua.user!),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -135,129 +220,25 @@ class _AuthorizedAccountScreen extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}).padding(all: 20),
|
}).padding(all: 20),
|
||||||
).padding(horizontal: 8, top: 16, bottom: 4),
|
).padding(horizontal: 8, top: 16, bottom: 4),
|
||||||
ListTile(
|
for (final item in AccountScreen.kNavList)
|
||||||
title: Text('accountPublishers').tr(),
|
Tooltip(
|
||||||
subtitle: Text('accountPublishersSubtitle').tr(),
|
message: item.subtitle.tr(),
|
||||||
|
child: ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
title: Text(item.title).tr(),
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
leading: const Icon(Symbols.face),
|
leading: Icon(item.icon),
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
GoRouter.of(context).pushNamed('accountPublishers');
|
GoRouter.of(context).pushNamed(item.screen);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
|
||||||
title: Text('accountProgram').tr(),
|
|
||||||
subtitle: Text('accountProgramDescription').tr(),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
leading: const Icon(Symbols.communities),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).pushNamed('accountProgram');
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
ListTile(
|
Tooltip(
|
||||||
title: Text('friends').tr(),
|
message: 'accountLogoutSubtitle'.tr(),
|
||||||
subtitle: Text('friendsDescription').tr(),
|
child: ListTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
leading: const Icon(Symbols.person),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).pushNamed('friend');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text('album').tr(),
|
|
||||||
subtitle: Text('albumDescription').tr(),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
leading: const Icon(Symbols.photo_library),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).pushNamed('album');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text('stickers').tr(),
|
|
||||||
subtitle: Text('stickersDescription').tr(),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
leading: const Icon(Symbols.emoji_emotions),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).pushNamed('stickers');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text('accountWallet').tr(),
|
|
||||||
subtitle: Text('accountWalletSubtitle').tr(),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
leading: const Icon(Symbols.wallet),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).pushNamed('accountWallet');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text('accountBadges').tr(),
|
|
||||||
subtitle: Text('accountBadgesDescription').tr(),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
leading: const Icon(Symbols.award_star),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).pushNamed('accountBadges');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text('accountKeyPairs').tr(),
|
|
||||||
subtitle: Text('accountKeyPairsDescription').tr(),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
leading: const Icon(Symbols.key),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).pushNamed('accountKeyPairs');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text('accountActionEvent').tr(),
|
|
||||||
subtitle: Text('accountActionEventDescription').tr(),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
leading: const Icon(Symbols.history),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).pushNamed('accountActionEvents');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text('accountAuthTickets').tr(),
|
|
||||||
subtitle: Text('accountAuthTicketsDescription').tr(),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
leading: const Icon(Symbols.confirmation_number),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).pushNamed('accountAuthTickets');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text('accountSettings').tr(),
|
|
||||||
subtitle: Text('accountSettingsSubtitle').tr(),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
leading: const Icon(Symbols.manage_accounts),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).pushNamed('accountSettings');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text('abuseReport').tr(),
|
|
||||||
subtitle: Text('abuseReportActionDescription').tr(),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
leading: const Icon(Symbols.flag),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).pushNamed('abuseReport');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text('accountLogout').tr(),
|
title: Text('accountLogout').tr(),
|
||||||
subtitle: Text('accountLogoutSubtitle').tr(),
|
minTileHeight: 48,
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
leading: const Icon(Symbols.logout),
|
leading: const Icon(Symbols.logout),
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
@@ -275,6 +256,7 @@ class _AuthorizedAccountScreen extends StatelessWidget {
|
|||||||
context.read<DatabaseProvider>().removeDatabase();
|
context.read<DatabaseProvider>().removeDatabase();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -59,6 +59,7 @@ class _ActionEventScreenState extends State<ActionEventScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const PageBackButton(),
|
leading: const PageBackButton(),
|
||||||
title: Text('accountActionEvent').tr(),
|
title: Text('accountActionEvent').tr(),
|
||||||
|
@@ -91,6 +91,7 @@ class _AccountAuthTicketState extends State<AccountAuthTicket> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const PageBackButton(),
|
leading: const PageBackButton(),
|
||||||
title: Text('accountAuthTickets').tr(),
|
title: Text('accountAuthTickets').tr(),
|
||||||
|
@@ -70,6 +70,7 @@ class _AccountBadgesScreenState extends State<AccountBadgesScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('screenAccountBadges').tr(),
|
title: Text('screenAccountBadges').tr(),
|
||||||
),
|
),
|
||||||
|
@@ -69,6 +69,7 @@ class _AccountContactMethodState extends State<AccountContactMethod> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const PageBackButton(),
|
leading: const PageBackButton(),
|
||||||
title: Text('accountContactMethods').tr(),
|
title: Text('accountContactMethods').tr(),
|
||||||
|
@@ -16,7 +16,11 @@ final Map<int, (String, String, IconData)> kFactorTypes = {
|
|||||||
0: ('authFactorPassword', 'authFactorPasswordDescription', Symbols.password),
|
0: ('authFactorPassword', 'authFactorPasswordDescription', Symbols.password),
|
||||||
1: ('authFactorEmail', 'authFactorEmailDescription', Symbols.email),
|
1: ('authFactorEmail', 'authFactorEmailDescription', Symbols.email),
|
||||||
2: ('authFactorTOTP', 'authFactorTOTPDescription', Symbols.timer),
|
2: ('authFactorTOTP', 'authFactorTOTPDescription', Symbols.timer),
|
||||||
3: ('authFactorInAppNotify', 'authFactorInAppNotifyDescription', Symbols.notifications_active),
|
3: (
|
||||||
|
'authFactorInAppNotify',
|
||||||
|
'authFactorInAppNotifyDescription',
|
||||||
|
Symbols.notifications_active
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
class FactorSettingsScreen extends StatefulWidget {
|
class FactorSettingsScreen extends StatefulWidget {
|
||||||
@@ -36,7 +40,10 @@ class _FactorSettingsScreenState extends State<FactorSettingsScreen> {
|
|||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
final resp = await sn.client.get('/cgi/id/users/me/factors');
|
final resp = await sn.client.get('/cgi/id/users/me/factors');
|
||||||
_factors = List<SnAuthFactor>.from(
|
_factors = List<SnAuthFactor>.from(
|
||||||
resp.data?.map((e) => SnAuthFactor.fromJson(e as Map<String, dynamic>)).toList() ?? [],
|
resp.data
|
||||||
|
?.map((e) => SnAuthFactor.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList() ??
|
||||||
|
[],
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
@@ -55,6 +62,7 @@ class _FactorSettingsScreenState extends State<FactorSettingsScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: PageBackButton(),
|
leading: PageBackButton(),
|
||||||
title: Text('screenFactorSettings').tr(),
|
title: Text('screenFactorSettings').tr(),
|
||||||
@@ -96,7 +104,8 @@ class _FactorSettingsScreenState extends State<FactorSettingsScreen> {
|
|||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(kFactorTypes[ele.type]!.$1).tr(),
|
title: Text(kFactorTypes[ele.type]!.$1).tr(),
|
||||||
subtitle: Text(kFactorTypes[ele.type]!.$2).tr(),
|
subtitle: Text(kFactorTypes[ele.type]!.$2).tr(),
|
||||||
contentPadding: const EdgeInsets.only(left: 24, right: 12),
|
contentPadding:
|
||||||
|
const EdgeInsets.only(left: 24, right: 12),
|
||||||
leading: Icon(kFactorTypes[ele.type]!.$3),
|
leading: Icon(kFactorTypes[ele.type]!.$3),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
icon: const Icon(Symbols.close),
|
icon: const Icon(Symbols.close),
|
||||||
@@ -105,14 +114,17 @@ class _FactorSettingsScreenState extends State<FactorSettingsScreen> {
|
|||||||
context
|
context
|
||||||
.showConfirmDialog(
|
.showConfirmDialog(
|
||||||
'authFactorDelete'.tr(),
|
'authFactorDelete'.tr(),
|
||||||
'authFactorDeleteDescription'.tr(args: [kFactorTypes[ele.type]!.$1.tr()]),
|
'authFactorDeleteDescription'.tr(
|
||||||
|
args: [kFactorTypes[ele.type]!.$1.tr()]),
|
||||||
)
|
)
|
||||||
.then((val) async {
|
.then((val) async {
|
||||||
if (!val) return;
|
if (!val) return;
|
||||||
try {
|
try {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn =
|
||||||
await sn.client.delete('/cgi/id/users/me/factors/${ele.id}');
|
context.read<SnNetworkProvider>();
|
||||||
|
await sn.client.delete(
|
||||||
|
'/cgi/id/users/me/factors/${ele.id}');
|
||||||
_fetchFactors();
|
_fetchFactors();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
@@ -191,7 +203,9 @@ class _FactorNewDialogState extends State<_FactorNewDialog> {
|
|||||||
value: _factorType,
|
value: _factorType,
|
||||||
items: kFactorTypes.entries.map(
|
items: kFactorTypes.entries.map(
|
||||||
(ele) {
|
(ele) {
|
||||||
final contains = widget.currentlyHave.map((ele) => ele.type).contains(ele.key);
|
final contains = widget.currentlyHave
|
||||||
|
.map((ele) => ele.type)
|
||||||
|
.contains(ele.key);
|
||||||
return DropdownMenuItem<int>(
|
return DropdownMenuItem<int>(
|
||||||
enabled: !contains,
|
enabled: !contains,
|
||||||
value: ele.key,
|
value: ele.key,
|
||||||
|
@@ -37,6 +37,7 @@ class _KeyPairScreenState extends State<KeyPairScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('screenKeyPairs').tr(),
|
title: Text('screenKeyPairs').tr(),
|
||||||
),
|
),
|
||||||
|
@@ -75,6 +75,7 @@ class _AccountNotifyPrefsScreenState extends State<AccountNotifyPrefsScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const PageBackButton(),
|
leading: const PageBackButton(),
|
||||||
title: Text('accountSettingsNotify').tr(),
|
title: Text('accountSettingsNotify').tr(),
|
||||||
|
@@ -70,6 +70,7 @@ class _AccountSecurityPrefsScreenState
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const PageBackButton(),
|
leading: const PageBackButton(),
|
||||||
title: Text('accountSettingsSecurity').tr(),
|
title: Text('accountSettingsSecurity').tr(),
|
||||||
|
@@ -66,21 +66,23 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
_locationController.text = prof.profile!.location;
|
_locationController.text = prof.profile!.location;
|
||||||
_avatar = prof.avatar;
|
_avatar = prof.avatar;
|
||||||
_banner = prof.banner;
|
_banner = prof.banner;
|
||||||
_links = prof.profile!.links.entries.map((ele) => (ele.key, ele.value)).toList();
|
_links =
|
||||||
|
prof.profile!.links.entries.map((ele) => (ele.key, ele.value)).toList();
|
||||||
_birthday = prof.profile!.birthday?.toLocal();
|
_birthday = prof.profile!.birthday?.toLocal();
|
||||||
if (_birthday != null) {
|
if (_birthday != null) {
|
||||||
_birthdayController.text = DateFormat(_kDateFormat).format(prof.profile!.birthday!.toLocal());
|
_birthdayController.text =
|
||||||
|
DateFormat(_kDateFormat).format(prof.profile!.birthday!.toLocal());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _selectBirthday() async {
|
void _selectBirthday() async {
|
||||||
await showCupertinoModalPopup<DateTime?>(
|
await showCupertinoModalPopup<DateTime?>(
|
||||||
context: context,
|
context: context,
|
||||||
builder:
|
builder: (BuildContext context) => Container(
|
||||||
(BuildContext context) => Container(
|
|
||||||
height: 216,
|
height: 216,
|
||||||
padding: const EdgeInsets.only(top: 6.0),
|
padding: const EdgeInsets.only(top: 6.0),
|
||||||
margin: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
margin:
|
||||||
|
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
@@ -91,7 +93,8 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
onDateTimeChanged: (DateTime newDate) {
|
onDateTimeChanged: (DateTime newDate) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_birthday = newDate;
|
_birthday = newDate;
|
||||||
_birthdayController.text = DateFormat(_kDateFormat).format(_birthday!);
|
_birthdayController.text =
|
||||||
|
DateFormat(_kDateFormat).format(_birthday!);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -109,11 +112,12 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
|
|
||||||
Uint8List? rawBytes;
|
Uint8List? rawBytes;
|
||||||
if (!skipCrop) {
|
if (!skipCrop) {
|
||||||
final ImageProvider imageProvider = kIsWeb ? NetworkImage(image.path) : FileImage(File(image.path));
|
final ImageProvider imageProvider =
|
||||||
final aspectRatios =
|
kIsWeb ? NetworkImage(image.path) : FileImage(File(image.path));
|
||||||
place == 'banner' ? [CropAspectRatio(width: 16, height: 7)] : [CropAspectRatio(width: 1, height: 1)];
|
final aspectRatios = place == 'banner'
|
||||||
final result =
|
? [CropAspectRatio(width: 16, height: 7)]
|
||||||
(!kIsWeb && (Platform.isIOS || Platform.isMacOS))
|
: [CropAspectRatio(width: 1, height: 1)];
|
||||||
|
final result = (!kIsWeb && (Platform.isIOS || Platform.isMacOS))
|
||||||
? await showCupertinoImageCropper(
|
? await showCupertinoImageCropper(
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
context,
|
context,
|
||||||
@@ -131,7 +135,9 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() => _isBusy = true);
|
setState(() => _isBusy = true);
|
||||||
rawBytes = (await result.uiImage.toByteData(format: ImageByteFormat.png))!.buffer.asUint8List();
|
rawBytes = (await result.uiImage.toByteData(format: ImageByteFormat.png))!
|
||||||
|
.buffer
|
||||||
|
.asUint8List();
|
||||||
} else {
|
} else {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() => _isBusy = true);
|
setState(() => _isBusy = true);
|
||||||
@@ -152,7 +158,8 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
await sn.client.put('/cgi/id/users/me/$place', data: {'attachment': attachment.rid});
|
await sn.client
|
||||||
|
.put('/cgi/id/users/me/$place', data: {'attachment': attachment.rid});
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final ua = context.read<UserProvider>();
|
final ua = context.read<UserProvider>();
|
||||||
@@ -188,7 +195,9 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
'location': _locationController.value.text,
|
'location': _locationController.value.text,
|
||||||
'birthday': _birthday?.toUtc().toIso8601String(),
|
'birthday': _birthday?.toUtc().toIso8601String(),
|
||||||
'links': {
|
'links': {
|
||||||
for (final link in _links!.where((ele) => ele.$1.isNotEmpty && ele.$2.isNotEmpty)) link.$1: link.$2,
|
for (final link in _links!
|
||||||
|
.where((ele) => ele.$1.isNotEmpty && ele.$2.isNotEmpty))
|
||||||
|
link.$1: link.$2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -235,7 +244,10 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
appBar: AppBar(leading: const PageBackButton(), title: Text('screenAccountProfileEdit').tr()),
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
|
appBar: AppBar(
|
||||||
|
leading: const PageBackButton(),
|
||||||
|
title: Text('screenAccountProfileEdit').tr()),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -251,12 +263,15 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 16 / 9,
|
aspectRatio: 16 / 7,
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
color: Theme.of(context)
|
||||||
child:
|
.colorScheme
|
||||||
_banner != null
|
.surfaceContainerHigh,
|
||||||
? AutoResizeUniversalImage(sn.getAttachmentUrl(_banner!), fit: BoxFit.cover)
|
child: _banner != null
|
||||||
|
? AutoResizeUniversalImage(
|
||||||
|
sn.getAttachmentUrl(_banner!),
|
||||||
|
fit: BoxFit.cover)
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -294,12 +309,16 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
labelText: 'fieldUsername'.tr(),
|
labelText: 'fieldUsername'.tr(),
|
||||||
helperText: 'fieldUsernameCannotEditHint'.tr(),
|
helperText: 'fieldUsernameCannotEditHint'.tr(),
|
||||||
),
|
),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _nicknameController,
|
controller: _nicknameController,
|
||||||
decoration: InputDecoration(border: const UnderlineInputBorder(), labelText: 'fieldNickname'.tr()),
|
decoration: InputDecoration(
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldNickname'.tr()),
|
||||||
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -311,7 +330,8 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
border: const UnderlineInputBorder(),
|
border: const UnderlineInputBorder(),
|
||||||
labelText: 'fieldFirstName'.tr(),
|
labelText: 'fieldFirstName'.tr(),
|
||||||
),
|
),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
@@ -323,7 +343,8 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
border: const UnderlineInputBorder(),
|
border: const UnderlineInputBorder(),
|
||||||
labelText: 'fieldLastName'.tr(),
|
labelText: 'fieldLastName'.tr(),
|
||||||
),
|
),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -338,7 +359,8 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
border: const UnderlineInputBorder(),
|
border: const UnderlineInputBorder(),
|
||||||
labelText: 'fieldGender'.tr(),
|
labelText: 'fieldGender'.tr(),
|
||||||
),
|
),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Gap(4),
|
const Gap(4),
|
||||||
@@ -350,7 +372,8 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
border: const UnderlineInputBorder(),
|
border: const UnderlineInputBorder(),
|
||||||
labelText: 'fieldPronouns'.tr(),
|
labelText: 'fieldPronouns'.tr(),
|
||||||
),
|
),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -360,8 +383,11 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
minLines: 3,
|
minLines: 3,
|
||||||
decoration: InputDecoration(border: const UnderlineInputBorder(), labelText: 'fieldDescription'.tr()),
|
decoration: InputDecoration(
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldDescription'.tr()),
|
||||||
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
@@ -373,18 +399,21 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
border: const UnderlineInputBorder(),
|
border: const UnderlineInputBorder(),
|
||||||
labelText: 'fieldTimeZone'.tr(),
|
labelText: 'fieldTimeZone'.tr(),
|
||||||
),
|
),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Gap(4),
|
const Gap(4),
|
||||||
StyledWidget(
|
StyledWidget(
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Symbols.calendar_month),
|
icon: const Icon(Symbols.calendar_month),
|
||||||
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
|
visualDensity:
|
||||||
|
VisualDensity(horizontal: -4, vertical: -4),
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
constraints: const BoxConstraints(),
|
constraints: const BoxConstraints(),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
_timezoneController.text = await FlutterTimezone.getLocalTimezone();
|
_timezoneController.text =
|
||||||
|
await FlutterTimezone.getLocalTimezone();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).padding(top: 6),
|
).padding(top: 6),
|
||||||
@@ -392,7 +421,8 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
StyledWidget(
|
StyledWidget(
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Symbols.clear),
|
icon: const Icon(Symbols.clear),
|
||||||
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
|
visualDensity:
|
||||||
|
VisualDensity(horizontal: -4, vertical: -4),
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
constraints: const BoxConstraints(),
|
constraints: const BoxConstraints(),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -404,13 +434,18 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
),
|
),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _locationController,
|
controller: _locationController,
|
||||||
decoration: InputDecoration(border: const UnderlineInputBorder(), labelText: 'fieldLocation'.tr()),
|
decoration: InputDecoration(
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldLocation'.tr()),
|
||||||
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _birthdayController,
|
controller: _birthdayController,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
decoration: InputDecoration(border: const UnderlineInputBorder(), labelText: 'fieldBirthday'.tr()),
|
decoration: InputDecoration(
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldBirthday'.tr()),
|
||||||
onTap: () => _selectBirthday(),
|
onTap: () => _selectBirthday(),
|
||||||
),
|
),
|
||||||
if (_links != null)
|
if (_links != null)
|
||||||
@@ -418,7 +453,8 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
margin: const EdgeInsets.only(top: 16, bottom: 4),
|
margin: const EdgeInsets.only(top: 16, bottom: 4),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16, vertical: 8),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -427,13 +463,17 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
'fieldLinks'.tr(),
|
'fieldLinks'.tr(),
|
||||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontSize: 17),
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleMedium!
|
||||||
|
.copyWith(fontSize: 17),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
constraints: const BoxConstraints(),
|
constraints: const BoxConstraints(),
|
||||||
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
|
visualDensity:
|
||||||
|
VisualDensity(horizontal: -4, vertical: -4),
|
||||||
icon: const Icon(Symbols.add),
|
icon: const Icon(Symbols.add),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() => _links!.add(('', '')));
|
setState(() => _links!.add(('', '')));
|
||||||
@@ -457,7 +497,9 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
_links![idx] = (value, _links![idx].$2);
|
_links![idx] = (value, _links![idx].$2);
|
||||||
},
|
},
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) => FocusManager
|
||||||
|
.instance.primaryFocus
|
||||||
|
?.unfocus(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
@@ -473,7 +515,9 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
|
|||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
_links![idx] = (_links![idx].$1, value);
|
_links![idx] = (_links![idx].$1, value);
|
||||||
},
|
},
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) => FocusManager
|
||||||
|
.instance.primaryFocus
|
||||||
|
?.unfocus(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:math' as math;
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@@ -60,6 +61,21 @@ final Map<String, (String, IconData, Color)> kBadgesMeta = {
|
|||||||
Symbols.thumb_up,
|
Symbols.thumb_up,
|
||||||
Colors.lightGreen,
|
Colors.lightGreen,
|
||||||
),
|
),
|
||||||
|
'programs.developers': (
|
||||||
|
'badgeProgramDeveloper',
|
||||||
|
Symbols.code,
|
||||||
|
Colors.blue,
|
||||||
|
),
|
||||||
|
'programs.stellar': (
|
||||||
|
'badgeProgramStellar',
|
||||||
|
Symbols.family_star,
|
||||||
|
Colors.orange,
|
||||||
|
),
|
||||||
|
'programs.moderator': (
|
||||||
|
'badgeProgramModerator',
|
||||||
|
Symbols.sword_rose,
|
||||||
|
Colors.blue,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
class UserScreen extends StatefulWidget {
|
class UserScreen extends StatefulWidget {
|
||||||
@@ -227,7 +243,7 @@ class _UserScreenState extends State<UserScreen>
|
|||||||
|
|
||||||
late final _appBarWidth = MediaQuery.of(context).size.width;
|
late final _appBarWidth = MediaQuery.of(context).size.width;
|
||||||
late final _appBarHeight =
|
late final _appBarHeight =
|
||||||
(_appBarWidth * kBannerAspectRatio).roundToDouble();
|
math.min((_appBarWidth * kBannerAspectRatio), 360).roundToDouble();
|
||||||
|
|
||||||
void _updateAppBarBlur() {
|
void _updateAppBarBlur() {
|
||||||
if (_scrollController.offset > _appBarHeight) return;
|
if (_scrollController.offset > _appBarHeight) return;
|
||||||
@@ -489,10 +505,10 @@ class _UserScreenState extends State<UserScreen>
|
|||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Wrap(
|
Wrap(
|
||||||
|
spacing: 4,
|
||||||
|
runSpacing: 4,
|
||||||
children: _account!.badges
|
children: _account!.badges
|
||||||
.map(
|
.map((ele) => AccountBadge(badge: ele))
|
||||||
(ele) => AccountBadge(badge: ele),
|
|
||||||
)
|
|
||||||
.toList(),
|
.toList(),
|
||||||
).padding(horizontal: 8),
|
).padding(horizontal: 8),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
|
@@ -9,6 +9,7 @@ import 'package:surface/providers/sn_network.dart';
|
|||||||
import 'package:surface/types/account.dart';
|
import 'package:surface/types/account.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/loading_indicator.dart';
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
|
import 'package:surface/widgets/markdown_content.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||||
|
|
||||||
class AccountProgramScreen extends StatefulWidget {
|
class AccountProgramScreen extends StatefulWidget {
|
||||||
@@ -69,6 +70,7 @@ class _AccountProgramScreenState extends State<AccountProgramScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('accountProgram').tr(),
|
title: Text('accountProgram').tr(),
|
||||||
),
|
),
|
||||||
@@ -86,14 +88,17 @@ class _AccountProgramScreenState extends State<AccountProgramScreen> {
|
|||||||
borderRadius: BorderRadius.all(Radius.circular(8)),
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
|
isScrollControlled: true,
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => _ProgramJoinPopup(
|
builder: (context) => _ProgramJoinPopup(
|
||||||
program: ele,
|
program: ele,
|
||||||
isJoined: _programMembers
|
isJoined:
|
||||||
.any((ele) => ele.programId == ele.id),
|
_programMembers.any((e) => e.programId == ele.id),
|
||||||
),
|
),
|
||||||
).then((value) {
|
).then((value) {
|
||||||
|
if (value == true) {
|
||||||
_fetchProgramMembers();
|
_fetchProgramMembers();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -137,7 +142,7 @@ class _AccountProgramScreenState extends State<AccountProgramScreen> {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
if (_programMembers
|
if (_programMembers
|
||||||
.any((ele) => ele.programId == ele.id))
|
.any((e) => e.programId == ele.id))
|
||||||
Text('accountProgramAlreadyJoined'.tr())
|
Text('accountProgramAlreadyJoined'.tr())
|
||||||
.opacity(0.75),
|
.opacity(0.75),
|
||||||
],
|
],
|
||||||
@@ -205,7 +210,10 @@ class _ProgramJoinPopupState extends State<_ProgramJoinPopup> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return SizedBox(
|
||||||
|
height: MediaQuery.of(context).size.height * 0.75,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
@@ -240,11 +248,7 @@ class _ProgramJoinPopupState extends State<_ProgramJoinPopup> {
|
|||||||
widget.program.name,
|
widget.program.name,
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
).bold(),
|
).bold(),
|
||||||
Text(
|
MarkdownTextContent(content: widget.program.description),
|
||||||
widget.program.description,
|
|
||||||
maxLines: 3,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Text(
|
Text(
|
||||||
'accountProgramJoinRequirements',
|
'accountProgramJoinRequirements',
|
||||||
@@ -278,7 +282,10 @@ class _ProgramJoinPopupState extends State<_ProgramJoinPopup> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 24),
|
).padding(horizontal: 24),
|
||||||
|
Gap(MediaQuery.of(context).padding.bottom),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,10 +27,12 @@ class AccountPublisherEditScreen extends StatefulWidget {
|
|||||||
const AccountPublisherEditScreen({super.key, required this.name});
|
const AccountPublisherEditScreen({super.key, required this.name});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AccountPublisherEditScreen> createState() => _AccountPublisherEditScreenState();
|
State<AccountPublisherEditScreen> createState() =>
|
||||||
|
_AccountPublisherEditScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AccountPublisherEditScreenState extends State<AccountPublisherEditScreen> {
|
class _AccountPublisherEditScreenState
|
||||||
|
extends State<AccountPublisherEditScreen> {
|
||||||
bool _isBusy = false;
|
bool _isBusy = false;
|
||||||
|
|
||||||
SnPublisher? _publisher;
|
SnPublisher? _publisher;
|
||||||
@@ -115,11 +117,12 @@ class _AccountPublisherEditScreenState extends State<AccountPublisherEditScreen>
|
|||||||
|
|
||||||
Uint8List? rawBytes;
|
Uint8List? rawBytes;
|
||||||
if (!skipCrop) {
|
if (!skipCrop) {
|
||||||
final ImageProvider imageProvider = kIsWeb ? NetworkImage(image.path) : FileImage(File(image.path));
|
final ImageProvider imageProvider =
|
||||||
final aspectRatios =
|
kIsWeb ? NetworkImage(image.path) : FileImage(File(image.path));
|
||||||
place == 'banner' ? [CropAspectRatio(width: 16, height: 7)] : [CropAspectRatio(width: 1, height: 1)];
|
final aspectRatios = place == 'banner'
|
||||||
final result =
|
? [CropAspectRatio(width: 16, height: 7)]
|
||||||
(!kIsWeb && (Platform.isIOS || Platform.isMacOS))
|
: [CropAspectRatio(width: 1, height: 1)];
|
||||||
|
final result = (!kIsWeb && (Platform.isIOS || Platform.isMacOS))
|
||||||
? await showCupertinoImageCropper(
|
? await showCupertinoImageCropper(
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
context,
|
context,
|
||||||
@@ -137,7 +140,9 @@ class _AccountPublisherEditScreenState extends State<AccountPublisherEditScreen>
|
|||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() => _isBusy = true);
|
setState(() => _isBusy = true);
|
||||||
rawBytes = (await result.uiImage.toByteData(format: ImageByteFormat.png))!.buffer.asUint8List();
|
rawBytes = (await result.uiImage.toByteData(format: ImageByteFormat.png))!
|
||||||
|
.buffer
|
||||||
|
.asUint8List();
|
||||||
} else {
|
} else {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() => _isBusy = true);
|
setState(() => _isBusy = true);
|
||||||
@@ -191,7 +196,10 @@ class _AccountPublisherEditScreenState extends State<AccountPublisherEditScreen>
|
|||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
appBar: AppBar(leading: PageBackButton(), title: Text('screenAccountPublisherEdit').tr()),
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
|
appBar: AppBar(
|
||||||
|
leading: PageBackButton(),
|
||||||
|
title: Text('screenAccountPublisherEdit').tr()),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -206,12 +214,15 @@ class _AccountPublisherEditScreenState extends State<AccountPublisherEditScreen>
|
|||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 16 / 9,
|
aspectRatio: 16 / 7,
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
color: Theme.of(context)
|
||||||
child:
|
.colorScheme
|
||||||
_banner != null
|
.surfaceContainerHigh,
|
||||||
? AutoResizeUniversalImage(sn.getAttachmentUrl(_banner!), fit: BoxFit.cover)
|
child: _banner != null
|
||||||
|
? AutoResizeUniversalImage(
|
||||||
|
sn.getAttachmentUrl(_banner!),
|
||||||
|
fit: BoxFit.cover)
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -245,13 +256,15 @@ class _AccountPublisherEditScreenState extends State<AccountPublisherEditScreen>
|
|||||||
labelText: 'fieldUsername'.tr(),
|
labelText: 'fieldUsername'.tr(),
|
||||||
helperText: 'fieldUsernameCannotEditHint'.tr(),
|
helperText: 'fieldUsernameCannotEditHint'.tr(),
|
||||||
),
|
),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const Gap(4),
|
const Gap(4),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _nickController,
|
controller: _nickController,
|
||||||
decoration: InputDecoration(labelText: 'fieldNickname'.tr()),
|
decoration: InputDecoration(labelText: 'fieldNickname'.tr()),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const Gap(4),
|
const Gap(4),
|
||||||
TextField(
|
TextField(
|
||||||
@@ -259,7 +272,8 @@ class _AccountPublisherEditScreenState extends State<AccountPublisherEditScreen>
|
|||||||
maxLines: null,
|
maxLines: null,
|
||||||
minLines: 3,
|
minLines: 3,
|
||||||
decoration: InputDecoration(labelText: 'fieldDescription'.tr()),
|
decoration: InputDecoration(labelText: 'fieldDescription'.tr()),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const Gap(12),
|
const Gap(12),
|
||||||
Row(
|
Row(
|
||||||
|
@@ -26,6 +26,7 @@ class _AccountPublisherNewScreenState extends State<AccountPublisherNewScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const PageBackButton(),
|
leading: const PageBackButton(),
|
||||||
title: Text('screenAccountPublisherNew').tr(),
|
title: Text('screenAccountPublisherNew').tr(),
|
||||||
|
@@ -33,7 +33,8 @@ class _PublisherScreenState extends State<PublisherScreen> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final resp = await sn.client.get('/cgi/co/publishers/me');
|
final resp = await sn.client.get('/cgi/co/publishers/me');
|
||||||
final List<SnPublisher> out = List<SnPublisher>.from(resp.data?.map((e) => SnPublisher.fromJson(e)) ?? []);
|
final List<SnPublisher> out = List<SnPublisher>.from(
|
||||||
|
resp.data?.map((e) => SnPublisher.fromJson(e)) ?? []);
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
@@ -81,6 +82,7 @@ class _PublisherScreenState extends State<PublisherScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const PageBackButton(),
|
leading: const PageBackButton(),
|
||||||
title: Text('screenAccountPublishers').tr(),
|
title: Text('screenAccountPublishers').tr(),
|
||||||
@@ -93,7 +95,9 @@ class _PublisherScreenState extends State<PublisherScreen> {
|
|||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
leading: const Icon(Symbols.add_circle),
|
leading: const Icon(Symbols.add_circle),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
GoRouter.of(context).pushNamed('accountPublisherNew').then((value) {
|
GoRouter.of(context)
|
||||||
|
.pushNamed('accountPublisherNew')
|
||||||
|
.then((value) {
|
||||||
if (value == true) {
|
if (value == true) {
|
||||||
_publishers.clear();
|
_publishers.clear();
|
||||||
_fetchPublishers();
|
_fetchPublishers();
|
||||||
@@ -119,7 +123,8 @@ class _PublisherScreenState extends State<PublisherScreen> {
|
|||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(publisher.nick),
|
title: Text(publisher.nick),
|
||||||
subtitle: Text('@${publisher.name}'),
|
subtitle: Text('@${publisher.name}'),
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 16),
|
||||||
leading: AccountImage(content: publisher.avatar),
|
leading: AccountImage(content: publisher.avatar),
|
||||||
trailing: PopupMenuButton(
|
trailing: PopupMenuButton(
|
||||||
itemBuilder: (BuildContext context) => [
|
itemBuilder: (BuildContext context) => [
|
||||||
|
187
lib/screens/account/punishments.dart
Normal file
187
lib/screens/account/punishments.dart
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:surface/providers/sn_network.dart';
|
||||||
|
import 'package:surface/types/account.dart';
|
||||||
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
|
import 'package:surface/widgets/dialog.dart';
|
||||||
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
|
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||||
|
|
||||||
|
const kPunishmentIcons = [
|
||||||
|
Symbols.warning,
|
||||||
|
Symbols.emergency_home,
|
||||||
|
Symbols.dangerous,
|
||||||
|
];
|
||||||
|
|
||||||
|
class PunishmentsScreen extends StatefulWidget {
|
||||||
|
const PunishmentsScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<PunishmentsScreen> createState() => _PunishmentsScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PunishmentsScreenState extends State<PunishmentsScreen> {
|
||||||
|
bool _isBusy = false;
|
||||||
|
List<SnPunishment>? _punishments;
|
||||||
|
|
||||||
|
Future<void> _fetchPunishments() async {
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
try {
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final resp = await sn.client.get('/cgi/id/punishments');
|
||||||
|
if (!mounted) return;
|
||||||
|
_punishments = List.from(
|
||||||
|
resp.data.map((ele) => SnPunishment.fromJson(ele)),
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
if (!mounted) return;
|
||||||
|
context.showErrorDialog(err);
|
||||||
|
} finally {
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_fetchPunishments();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('accountPunishments').tr(),
|
||||||
|
leading: PageBackButton(),
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
LoadingIndicator(isActive: _isBusy),
|
||||||
|
Card(
|
||||||
|
margin: EdgeInsets.only(bottom: 8, left: 8, right: 8),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(Symbols.visibility, size: 20),
|
||||||
|
const Gap(6),
|
||||||
|
Expanded(
|
||||||
|
child: Text('punishmentOverall').tr().fontSize(16).bold(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
if (_punishments == null) return Text('loading').tr();
|
||||||
|
if (_punishments!.any((ele) => ele.type == 2)) {
|
||||||
|
return Text('punishmentStatusBanned').tr();
|
||||||
|
}
|
||||||
|
if (_punishments!.any(
|
||||||
|
(ele) => ele.type == 1 && ele.permNodes.isEmpty,
|
||||||
|
)) {
|
||||||
|
return Text('punishmentStatusLimitedFully').tr();
|
||||||
|
} else if (_punishments!.any((ele) => ele.type == 1)) {
|
||||||
|
return Text('punishmentStatusLimited').tr();
|
||||||
|
}
|
||||||
|
if (_punishments!.any((ele) => ele.type == 0)) {
|
||||||
|
return Text('punishmentStatusWarned').tr();
|
||||||
|
}
|
||||||
|
return Text('punishmentStatusNormal').tr();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 24, vertical: 16),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: RefreshIndicator(
|
||||||
|
onRefresh: _fetchPunishments,
|
||||||
|
child: ListView.separated(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
itemCount: _punishments?.length ?? 0,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final ele = _punishments![index];
|
||||||
|
return Card(
|
||||||
|
margin: EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(kPunishmentIcons[ele.type], size: 20),
|
||||||
|
const Gap(6),
|
||||||
|
Expanded(
|
||||||
|
child: Text('punishmentType${ele.type}')
|
||||||
|
.tr()
|
||||||
|
.fontSize(16)
|
||||||
|
.bold(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text(ele.reason),
|
||||||
|
const Gap(4),
|
||||||
|
Text(
|
||||||
|
'punishmentCreatedAt'.tr(args: [
|
||||||
|
DateFormat().format(
|
||||||
|
ele.createdAt.toLocal(),
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
).opacity(0.8),
|
||||||
|
Text(
|
||||||
|
ele.expiredAt == null
|
||||||
|
? 'punishmentExpiredNever'.tr()
|
||||||
|
: 'punishmentExpiredAt'.tr(args: [
|
||||||
|
DateFormat().format(
|
||||||
|
ele.expiredAt!.toLocal(),
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
).opacity(0.8),
|
||||||
|
const Gap(8),
|
||||||
|
if (ele.moderator != null)
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('punishmentModerator').tr().opacity(0.75),
|
||||||
|
InkWell(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
AccountImage(
|
||||||
|
content: ele.moderator!.avatar,
|
||||||
|
radius: 8,
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Text(ele.moderator?.nick ?? 'unknown'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context).pushNamed(
|
||||||
|
'accountProfilePage',
|
||||||
|
pathParameters: {
|
||||||
|
'name': ele.moderator!.name,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Text('punishmentMadeBySystem').tr().opacity(0.75),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 24, vertical: 16),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (_, __) => const Gap(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -37,6 +37,7 @@ class AccountSettingsScreen extends StatelessWidget {
|
|||||||
final ua = context.watch<UserProvider>();
|
final ua = context.watch<UserProvider>();
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: PageBackButton(),
|
leading: PageBackButton(),
|
||||||
title: Text('screenAccountSettings').tr(),
|
title: Text('screenAccountSettings').tr(),
|
@@ -8,7 +8,6 @@ import 'package:material_symbols_icons/symbols.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/providers/user_directory.dart';
|
|
||||||
import 'package:surface/types/attachment.dart';
|
import 'package:surface/types/attachment.dart';
|
||||||
import 'package:surface/widgets/attachment/attachment_zoom.dart';
|
import 'package:surface/widgets/attachment/attachment_zoom.dart';
|
||||||
import 'package:surface/widgets/attachment/attachment_item.dart';
|
import 'package:surface/widgets/attachment/attachment_item.dart';
|
||||||
@@ -53,7 +52,6 @@ class _AlbumScreenState extends State<AlbumScreen> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
final ud = context.read<UserDirectoryProvider>();
|
|
||||||
final resp = await sn.client.get('/cgi/uc/attachments', queryParameters: {
|
final resp = await sn.client.get('/cgi/uc/attachments', queryParameters: {
|
||||||
'take': 10,
|
'take': 10,
|
||||||
'offset': _attachments.length,
|
'offset': _attachments.length,
|
||||||
@@ -64,8 +62,6 @@ class _AlbumScreenState extends State<AlbumScreen> {
|
|||||||
_attachments.addAll(attachments);
|
_attachments.addAll(attachments);
|
||||||
_heroTags.addAll(_attachments.map((_) => uuid.v4()));
|
_heroTags.addAll(_attachments.map((_) => uuid.v4()));
|
||||||
|
|
||||||
await ud.listAccount(attachments.map((e) => e.accountId).toSet());
|
|
||||||
|
|
||||||
_totalCount = resp.data['count'] as int?;
|
_totalCount = resp.data['count'] as int?;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
import 'package:animations/animations.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
|
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
|
||||||
@@ -6,21 +8,22 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:responsive_framework/responsive_framework.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/channel.dart';
|
import 'package:surface/providers/channel.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
|
import 'package:surface/providers/sn_realm.dart';
|
||||||
import 'package:surface/providers/user_directory.dart';
|
import 'package:surface/providers/user_directory.dart';
|
||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/screens/chat/room.dart';
|
|
||||||
import 'package:surface/types/chat.dart';
|
import 'package:surface/types/chat.dart';
|
||||||
|
import 'package:surface/types/realm.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
import 'package:surface/widgets/account/account_select.dart';
|
import 'package:surface/widgets/account/account_select.dart';
|
||||||
import 'package:surface/widgets/app_bar_leading.dart';
|
import 'package:surface/widgets/app_bar_leading.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/loading_indicator.dart';
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
import 'package:surface/widgets/navigation/app_background.dart';
|
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||||
import 'package:surface/widgets/unauthorized_hint.dart';
|
import 'package:surface/widgets/unauthorized_hint.dart';
|
||||||
|
import 'package:surface/widgets/universal_image.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class ChatScreen extends StatefulWidget {
|
class ChatScreen extends StatefulWidget {
|
||||||
@@ -38,6 +41,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
List<SnChannel>? _channels;
|
List<SnChannel>? _channels;
|
||||||
Map<int, SnChatMessage>? _lastMessages;
|
Map<int, SnChatMessage>? _lastMessages;
|
||||||
Map<int, int>? _unreadCounts;
|
Map<int, int>? _unreadCounts;
|
||||||
|
Map<int, int>? _unreadCountsGrouped;
|
||||||
|
|
||||||
Future<void> _fetchWhatsNew() async {
|
Future<void> _fetchWhatsNew() async {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
@@ -45,19 +49,48 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
if (resp.data == null) return;
|
if (resp.data == null) return;
|
||||||
final List<dynamic> out = resp.data;
|
final List<dynamic> out = resp.data;
|
||||||
setState(() {
|
setState(() {
|
||||||
_unreadCounts = {for (var v in out) v['channel_id']: v['count']};
|
_unreadCounts ??= {};
|
||||||
|
_unreadCountsGrouped ??= {};
|
||||||
|
for (var v in out) {
|
||||||
|
_unreadCounts![v['channel_id']] = v['count'];
|
||||||
|
final channel =
|
||||||
|
_channels?.firstWhereOrNull((ele) => ele.id == v['channel_id']);
|
||||||
|
if (channel != null) {
|
||||||
|
if (channel.realmId != null) {
|
||||||
|
_unreadCountsGrouped![channel.realmId!] ??= 0;
|
||||||
|
_unreadCountsGrouped![channel.realmId!] =
|
||||||
|
(_unreadCountsGrouped![channel.realmId!]! + v['count']).toInt();
|
||||||
|
}
|
||||||
|
if (channel.type == 1) {
|
||||||
|
_unreadCountsGrouped![0] ??= 0;
|
||||||
|
_unreadCountsGrouped![0] =
|
||||||
|
(_unreadCountsGrouped![0]! + v['count']).toInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _refreshChannels({bool noRemote = false}) {
|
void _refreshChannels({bool withBoost = false, bool noRemote = false}) {
|
||||||
|
final ct = context.read<ChatChannelProvider>();
|
||||||
final ua = context.read<UserProvider>();
|
final ua = context.read<UserProvider>();
|
||||||
if (!ua.isAuthorized) {
|
if (!ua.isAuthorized) {
|
||||||
setState(() => _isBusy = false);
|
setState(() => _isBusy = false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!withBoost) {
|
||||||
|
if (!noRemote) {
|
||||||
|
ct.refreshAvailableChannels();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_channels = ct.availableChannels;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
final chan = context.read<ChatChannelProvider>();
|
final chan = context.read<ChatChannelProvider>();
|
||||||
chan.fetchChannels(noRemote: noRemote).listen((channels) async {
|
chan.fetchChannels(noRemote: true).listen((channels) async {
|
||||||
final lastMessages = await chan.getLastMessages(channels);
|
final lastMessages = await chan.getLastMessages(channels);
|
||||||
_lastMessages = {for (final val in lastMessages) val.channelId: val};
|
_lastMessages = {for (final val in lastMessages) val.channelId: val};
|
||||||
channels.sort((a, b) {
|
channels.sort((a, b) {
|
||||||
@@ -99,6 +132,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
..onDone(() {
|
..onDone(() {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() => _isBusy = false);
|
setState(() => _isBusy = false);
|
||||||
|
_fetchWhatsNew();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,22 +164,28 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SnChannel? _focusChannel;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_refreshChannels();
|
_refreshChannels(withBoost: true);
|
||||||
_fetchWhatsNew();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onTapChannel(SnChannel channel) {
|
void _onTapChannel(SnChannel channel) {
|
||||||
final doExpand = ResponsiveBreakpoints.of(context).largerOrEqualTo(DESKTOP);
|
setState(() => _unreadCounts?[channel.id] = 0);
|
||||||
|
if (ResponsiveScaffold.getIsExpand(context)) {
|
||||||
if (doExpand) {
|
GoRouter.of(context).pushReplacementNamed(
|
||||||
setState(() => _focusChannel = channel);
|
'chatRoom',
|
||||||
return;
|
pathParameters: {
|
||||||
|
'scope': channel.realm?.alias ?? 'global',
|
||||||
|
'alias': channel.alias,
|
||||||
|
},
|
||||||
|
).then((value) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() => _unreadCounts?[channel.id] = 0);
|
||||||
|
_refreshChannels(noRemote: true);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
GoRouter.of(context).pushNamed(
|
GoRouter.of(context).pushNamed(
|
||||||
'chatRoom',
|
'chatRoom',
|
||||||
pathParameters: {
|
pathParameters: {
|
||||||
@@ -154,16 +194,21 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
},
|
},
|
||||||
).then((value) {
|
).then((value) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_unreadCounts?[channel.id] = 0;
|
|
||||||
setState(() => _unreadCounts?[channel.id] = 0);
|
setState(() => _unreadCounts?[channel.id] = 0);
|
||||||
_refreshChannels(noRemote: true);
|
_refreshChannels(noRemote: true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SnRealm? _focusedRealm;
|
||||||
|
bool _isDirect = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ua = context.read<UserProvider>();
|
final ua = context.read<UserProvider>();
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final rel = context.read<SnRealmProvider>();
|
||||||
|
|
||||||
if (!ua.isAuthorized) {
|
if (!ua.isAuthorized) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
@@ -177,10 +222,8 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final doExpand = ResponsiveBreakpoints.of(context).largerOrEqualTo(DESKTOP);
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
final chatList = AppScaffold(
|
|
||||||
noBackground: doExpand,
|
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: AutoAppBarLeading(),
|
leading: AutoAppBarLeading(),
|
||||||
title: Text('screenChat').tr(),
|
title: Text('screenChat').tr(),
|
||||||
@@ -248,65 +291,199 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
LoadingIndicator(isActive: _isBusy),
|
LoadingIndicator(isActive: _isBusy),
|
||||||
|
if (_channels != null && ResponsiveScaffold.getIsExpand(context))
|
||||||
Expanded(
|
Expanded(
|
||||||
child: MediaQuery.removePadding(
|
|
||||||
context: context,
|
|
||||||
removeTop: true,
|
|
||||||
child: RefreshIndicator(
|
child: RefreshIndicator(
|
||||||
onRefresh: () => Future.wait([
|
onRefresh: () => Future.sync(() => _refreshChannels()),
|
||||||
Future.sync(() => _refreshChannels()),
|
child: Builder(builder: (context) {
|
||||||
_fetchWhatsNew(),
|
final scopeList = ListView(
|
||||||
]),
|
key: const Key('realm-list-view'),
|
||||||
child: ListView.builder(
|
padding: EdgeInsets.zero,
|
||||||
itemCount: _channels?.length ?? 0,
|
|
||||||
itemBuilder: (context, idx) {
|
|
||||||
final channel = _channels![idx];
|
|
||||||
final lastMessage = _lastMessages?[channel.id];
|
|
||||||
|
|
||||||
return _ChatChannelEntry(
|
|
||||||
channel: channel,
|
|
||||||
lastMessage: lastMessage,
|
|
||||||
unreadCount: _unreadCounts?[channel.id],
|
|
||||||
onTap: () {
|
|
||||||
if (doExpand) {
|
|
||||||
_unreadCounts?[channel.id] = 0;
|
|
||||||
setState(() => _focusChannel = channel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_onTapChannel(channel);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (doExpand) {
|
|
||||||
return AppBackground(
|
|
||||||
isRoot: true,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
children: [
|
||||||
SizedBox(width: 340, child: chatList),
|
ListTile(
|
||||||
const VerticalDivider(width: 1),
|
minTileHeight: 48,
|
||||||
if (_focusChannel != null)
|
leading:
|
||||||
|
const Icon(Symbols.inbox_text).padding(right: 4),
|
||||||
|
contentPadding: EdgeInsets.only(left: 24, right: 24),
|
||||||
|
title: Text('chatDirect').tr(),
|
||||||
|
trailing: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
if (_unreadCountsGrouped?[0] != null &&
|
||||||
|
(_unreadCountsGrouped?[0] ?? 0) > 0)
|
||||||
|
Badge(
|
||||||
|
label: Text(
|
||||||
|
_unreadCountsGrouped![0].toString(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
setState(() => _isDirect = true);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
...rel.availableRealms.map((ele) {
|
||||||
|
return ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
contentPadding: EdgeInsets.only(left: 20, right: 24),
|
||||||
|
leading: AccountImage(
|
||||||
|
content: ele.avatar,
|
||||||
|
radius: 16,
|
||||||
|
),
|
||||||
|
trailing: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
if (_unreadCountsGrouped?[ele.id] != null &&
|
||||||
|
(_unreadCountsGrouped?[ele.id] ?? 0) > 0)
|
||||||
|
Badge(
|
||||||
|
label: Text(
|
||||||
|
_unreadCountsGrouped![ele.id].toString(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
title: Text(ele.name),
|
||||||
|
onTap: () {
|
||||||
|
setState(() => _focusedRealm = ele);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
final directChatList = ListView(
|
||||||
|
key: Key('direct-chat-list-view'),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
leading: const Icon(Symbols.arrow_left_alt),
|
||||||
|
contentPadding: EdgeInsets.only(left: 24),
|
||||||
|
title: Text('back').tr(),
|
||||||
|
onTap: () {
|
||||||
|
setState(() => _isDirect = false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
|
..._channels!.where((ele) => ele.type == 1).map(
|
||||||
|
(ele) {
|
||||||
|
return _ChatChannelEntry(
|
||||||
|
channel: ele,
|
||||||
|
unreadCount: _unreadCounts?[ele.id],
|
||||||
|
lastMessage: _lastMessages?[ele.id],
|
||||||
|
isCompact: true,
|
||||||
|
onTap: () => _onTapChannel(ele),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
final realmScopedChatList = _focusedRealm == null
|
||||||
|
? const SizedBox.shrink()
|
||||||
|
: ListView(
|
||||||
|
key: ValueKey(_focusedRealm),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
children: [
|
||||||
|
if (_focusedRealm!.banner != null)
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: 16 / 7,
|
||||||
|
child: AutoResizeUniversalImage(
|
||||||
|
sn.getAttachmentUrl(
|
||||||
|
_focusedRealm!.banner!,
|
||||||
|
),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
tileColor: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surfaceContainer,
|
||||||
|
leading: AccountImage(
|
||||||
|
content: _focusedRealm!.avatar,
|
||||||
|
radius: 16,
|
||||||
|
),
|
||||||
|
contentPadding: EdgeInsets.only(
|
||||||
|
left: 20,
|
||||||
|
right: 16,
|
||||||
|
),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: const Icon(Symbols.close),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
onPressed: () {
|
||||||
|
setState(() => _focusedRealm = null);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
title: Text(_focusedRealm!.name),
|
||||||
|
),
|
||||||
|
...(_channels!
|
||||||
|
.where(
|
||||||
|
(ele) => ele.realmId == _focusedRealm?.id)
|
||||||
|
.map(
|
||||||
|
(ele) {
|
||||||
|
return _ChatChannelEntry(
|
||||||
|
channel: ele,
|
||||||
|
unreadCount: _unreadCounts?[ele.id],
|
||||||
|
lastMessage: _lastMessages?[ele.id],
|
||||||
|
onTap: () => _onTapChannel(ele),
|
||||||
|
isCompact: true,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
))
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
return PageTransitionSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
transitionBuilder: (Widget child,
|
||||||
|
Animation<double> primaryAnimation,
|
||||||
|
Animation<double> secondaryAnimation) {
|
||||||
|
return SharedAxisTransition(
|
||||||
|
animation: primaryAnimation,
|
||||||
|
secondaryAnimation: secondaryAnimation,
|
||||||
|
fillColor: Colors.transparent,
|
||||||
|
transitionType: SharedAxisTransitionType.horizontal,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: (_focusedRealm == null && !_isDirect)
|
||||||
|
? scopeList
|
||||||
|
: _isDirect
|
||||||
|
? directChatList
|
||||||
|
: realmScopedChatList,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else if (_channels != null)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ChatRoomScreen(
|
child: RefreshIndicator(
|
||||||
key: ValueKey(_focusChannel!.id),
|
onRefresh: () => Future.sync(() => _refreshChannels()),
|
||||||
scope: _focusChannel!.realm?.alias ?? 'global',
|
child: ListView(
|
||||||
alias: _focusChannel!.alias,
|
key: const Key('chat-list-view'),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
children: [
|
||||||
|
...(_channels!.map((ele) {
|
||||||
|
return _ChatChannelEntry(
|
||||||
|
channel: ele,
|
||||||
|
unreadCount: _unreadCounts?[ele.id],
|
||||||
|
lastMessage: _lastMessages?[ele.id],
|
||||||
|
onTap: () => _onTapChannel(ele),
|
||||||
|
);
|
||||||
|
}))
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return chatList;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ChatChannelEntry extends StatelessWidget {
|
class _ChatChannelEntry extends StatelessWidget {
|
||||||
@@ -314,11 +491,13 @@ class _ChatChannelEntry extends StatelessWidget {
|
|||||||
final int? unreadCount;
|
final int? unreadCount;
|
||||||
final SnChatMessage? lastMessage;
|
final SnChatMessage? lastMessage;
|
||||||
final Function? onTap;
|
final Function? onTap;
|
||||||
|
final bool isCompact;
|
||||||
const _ChatChannelEntry({
|
const _ChatChannelEntry({
|
||||||
required this.channel,
|
required this.channel,
|
||||||
this.unreadCount,
|
this.unreadCount,
|
||||||
this.lastMessage,
|
this.lastMessage,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
|
this.isCompact = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -337,6 +516,34 @@ class _ChatChannelEntry extends StatelessWidget {
|
|||||||
? ud.getFromCache(otherMember.accountId)?.nick ?? channel.name
|
? ud.getFromCache(otherMember.accountId)?.nick ?? channel.name
|
||||||
: channel.name;
|
: channel.name;
|
||||||
|
|
||||||
|
if (isCompact) {
|
||||||
|
return ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.only(left: otherMember != null ? 20 : 24, right: 24),
|
||||||
|
leading: otherMember != null
|
||||||
|
? AccountImage(
|
||||||
|
content: ud.getFromCache(otherMember.accountId)?.avatar,
|
||||||
|
radius: 16,
|
||||||
|
)
|
||||||
|
: const Icon(Symbols.tag),
|
||||||
|
trailing: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
if (unreadCount != null && (unreadCount ?? 0) > 0)
|
||||||
|
Badge(
|
||||||
|
label: Text(unreadCount.toString()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
title: Text(title),
|
||||||
|
onTap: () {
|
||||||
|
onTap?.call();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -399,7 +606,7 @@ class _ChatChannelEntry extends StatelessWidget {
|
|||||||
content: otherMember != null
|
content: otherMember != null
|
||||||
? ud.getFromCache(otherMember.accountId)?.avatar
|
? ud.getFromCache(otherMember.accountId)?.avatar
|
||||||
: channel.realm?.avatar,
|
: channel.realm?.avatar,
|
||||||
fallbackWidget: const Icon(Symbols.chat, size: 20),
|
fallbackWidget: const Icon(Symbols.tag, size: 20),
|
||||||
),
|
),
|
||||||
onTap: () => onTap?.call(),
|
onTap: () => onTap?.call(),
|
||||||
);
|
);
|
||||||
|
@@ -32,17 +32,16 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildListLayout() {
|
Widget _buildMeetLayout() {
|
||||||
final call = context.read<ChatCallProvider>();
|
final call = context.read<ChatCallProvider>();
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
color: Theme.of(context).colorScheme.surfaceContainer.withOpacity(0.75),
|
color:
|
||||||
|
Theme.of(context).colorScheme.surfaceContainer.withOpacity(0.75),
|
||||||
child: call.focusTrack != null
|
child: call.focusTrack != null
|
||||||
? InteractiveParticipantWidget(
|
? InteractiveParticipantWidget(
|
||||||
isFixedAvatar: false,
|
|
||||||
participant: call.focusTrack!,
|
participant: call.focusTrack!,
|
||||||
onTap: () {},
|
|
||||||
)
|
)
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
@@ -61,23 +60,19 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
|
|||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Padding(
|
return SizedBox(
|
||||||
padding: const EdgeInsets.only(top: 8, left: 8),
|
height: 128,
|
||||||
child: ClipRRect(
|
width: 128,
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
|
||||||
child: InteractiveParticipantWidget(
|
child: InteractiveParticipantWidget(
|
||||||
isFixedAvatar: true,
|
|
||||||
width: 120,
|
|
||||||
height: 120,
|
|
||||||
color: Theme.of(context).cardColor,
|
|
||||||
participant: track,
|
participant: track,
|
||||||
|
avatarSize: 32,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (track.participant.sid != call.focusTrack?.participant.sid) {
|
if (track.participant.sid !=
|
||||||
|
call.focusTrack?.participant.sid) {
|
||||||
call.setFocusTrack(track);
|
call.setFocusTrack(track);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -87,46 +82,26 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildGridLayout() {
|
Widget _buildListLayout() {
|
||||||
final call = context.read<ChatCallProvider>();
|
final call = context.read<ChatCallProvider>();
|
||||||
|
|
||||||
return LayoutBuilder(builder: (context, constraints) {
|
return LayoutBuilder(
|
||||||
double screenWidth = constraints.maxWidth;
|
builder: (context, constraints) {
|
||||||
double screenHeight = constraints.maxHeight;
|
return ListView.builder(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
int columns = (math.sqrt(call.participantTracks.length)).ceil();
|
|
||||||
int rows = (call.participantTracks.length / columns).ceil();
|
|
||||||
|
|
||||||
double tileWidth = screenWidth / columns;
|
|
||||||
double tileHeight = screenHeight / rows;
|
|
||||||
|
|
||||||
return StyledWidget(GridView.builder(
|
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: columns,
|
|
||||||
childAspectRatio: tileWidth / tileHeight,
|
|
||||||
crossAxisSpacing: 8,
|
|
||||||
mainAxisSpacing: 8,
|
|
||||||
),
|
|
||||||
itemCount: math.max(0, call.participantTracks.length),
|
itemCount: math.max(0, call.participantTracks.length),
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
final track = call.participantTracks[index];
|
final track = call.participantTracks[index];
|
||||||
return Card(
|
return InteractiveParticipantWidget(
|
||||||
child: ClipRRect(
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
isList: true,
|
||||||
child: InteractiveParticipantWidget(
|
avatarSize: 24,
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerHigh.withOpacity(0.75),
|
|
||||||
participant: track,
|
participant: track,
|
||||||
onTap: () {
|
|
||||||
if (track.participant.sid != call.focusTrack?.participant.sid) {
|
|
||||||
call.setFocusTrack(track);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)).padding(all: 8);
|
);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -149,6 +124,7 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
|
|||||||
listenable: call,
|
listenable: call,
|
||||||
builder: (context, _) {
|
builder: (context, _) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: RichText(
|
title: RichText(
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
@@ -169,9 +145,7 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
|
|||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: GestureDetector(
|
body: Column(
|
||||||
behavior: HitTestBehavior.translucent,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: MediaQuery.of(context).size.width,
|
width: MediaQuery.of(context).size.width,
|
||||||
@@ -183,7 +157,8 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
|
|||||||
Builder(builder: (context) {
|
Builder(builder: (context) {
|
||||||
final call = context.read<ChatCallProvider>();
|
final call = context.read<ChatCallProvider>();
|
||||||
final connectionQuality =
|
final connectionQuality =
|
||||||
call.room.localParticipant?.connectionQuality ?? livekit.ConnectionQuality.unknown;
|
call.room.localParticipant?.connectionQuality ??
|
||||||
|
livekit.ConnectionQuality.unknown;
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -205,24 +180,35 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
{
|
{
|
||||||
livekit.ConnectionState.disconnected: 'callStatusDisconnected'.tr(),
|
livekit.ConnectionState.disconnected:
|
||||||
livekit.ConnectionState.connected: 'callStatusConnected'.tr(),
|
'callStatusDisconnected'.tr(),
|
||||||
livekit.ConnectionState.connecting: 'callStatusConnecting'.tr(),
|
livekit.ConnectionState.connected:
|
||||||
livekit.ConnectionState.reconnecting: 'callStatusReconnecting'.tr(),
|
'callStatusConnected'.tr(),
|
||||||
|
livekit.ConnectionState.connecting:
|
||||||
|
'callStatusConnecting'.tr(),
|
||||||
|
livekit.ConnectionState.reconnecting:
|
||||||
|
'callStatusReconnecting'.tr(),
|
||||||
}[call.room.connectionState]!,
|
}[call.room.connectionState]!,
|
||||||
),
|
),
|
||||||
const Gap(6),
|
const Gap(6),
|
||||||
if (connectionQuality != livekit.ConnectionQuality.unknown)
|
if (connectionQuality !=
|
||||||
|
livekit.ConnectionQuality.unknown)
|
||||||
Icon(
|
Icon(
|
||||||
{
|
{
|
||||||
livekit.ConnectionQuality.excellent: Icons.signal_cellular_alt,
|
livekit.ConnectionQuality.excellent:
|
||||||
livekit.ConnectionQuality.good: Icons.signal_cellular_alt_2_bar,
|
Icons.signal_cellular_alt,
|
||||||
livekit.ConnectionQuality.poor: Icons.signal_cellular_alt_1_bar,
|
livekit.ConnectionQuality.good:
|
||||||
|
Icons.signal_cellular_alt_2_bar,
|
||||||
|
livekit.ConnectionQuality.poor:
|
||||||
|
Icons.signal_cellular_alt_1_bar,
|
||||||
}[connectionQuality],
|
}[connectionQuality],
|
||||||
color: {
|
color: {
|
||||||
livekit.ConnectionQuality.excellent: Colors.green,
|
livekit.ConnectionQuality.excellent:
|
||||||
livekit.ConnectionQuality.good: Colors.orange,
|
Colors.green,
|
||||||
livekit.ConnectionQuality.poor: Colors.red,
|
livekit.ConnectionQuality.good:
|
||||||
|
Colors.orange,
|
||||||
|
livekit.ConnectionQuality.poor:
|
||||||
|
Colors.red,
|
||||||
}[connectionQuality],
|
}[connectionQuality],
|
||||||
size: 16,
|
size: 16,
|
||||||
)
|
)
|
||||||
@@ -233,6 +219,7 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
|
|||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
),
|
),
|
||||||
).padding(all: 3),
|
).padding(all: 3),
|
||||||
],
|
],
|
||||||
@@ -244,7 +231,9 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: _layoutMode == 0 ? const Icon(Icons.view_list) : const Icon(Icons.grid_view),
|
icon: _layoutMode == 0
|
||||||
|
? const Icon(Icons.view_list)
|
||||||
|
: const Icon(Icons.grid_view),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_switchLayout();
|
_switchLayout();
|
||||||
},
|
},
|
||||||
@@ -261,9 +250,9 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
|
|||||||
builder: (context) {
|
builder: (context) {
|
||||||
switch (_layoutMode) {
|
switch (_layoutMode) {
|
||||||
case 1:
|
case 1:
|
||||||
return _buildGridLayout();
|
|
||||||
default:
|
|
||||||
return _buildListLayout();
|
return _buildListLayout();
|
||||||
|
default:
|
||||||
|
return _buildMeetLayout();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -277,10 +266,9 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
|
|||||||
call.room.localParticipant!,
|
call.room.localParticipant!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Gap(MediaQuery.of(context).padding.bottom),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () {},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -220,6 +220,7 @@ class _ChannelDetailScreenState extends State<ChannelDetailScreen> {
|
|||||||
final isOwned = ua.isAuthorized && _channel?.accountId == ua.user?.id;
|
final isOwned = ua.isAuthorized && _channel?.accountId == ua.user?.id;
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: _channel != null ? Text(_channel!.name) : Text('loading').tr(),
|
title: _channel != null ? Text(_channel!.name) : Text('loading').tr(),
|
||||||
),
|
),
|
||||||
|
@@ -49,7 +49,8 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
|
|||||||
resp.data?.map((e) => SnRealm.fromJson(e)) ?? [],
|
resp.data?.map((e) => SnRealm.fromJson(e)) ?? [],
|
||||||
);
|
);
|
||||||
if (_editingChannel != null) {
|
if (_editingChannel != null) {
|
||||||
_belongToRealm = _realms?.firstWhereOrNull((e) => e.id == _editingChannel!.realmId);
|
_belongToRealm =
|
||||||
|
_realms?.firstWhereOrNull((e) => e.id == _editingChannel!.realmId);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (mounted) context.showErrorDialog(err);
|
if (mounted) context.showErrorDialog(err);
|
||||||
@@ -97,7 +98,8 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
|
|||||||
'is_community': _isCommunity,
|
'is_community': _isCommunity,
|
||||||
if (_editingChannel != null && _belongToRealm == null)
|
if (_editingChannel != null && _belongToRealm == null)
|
||||||
'new_belongs_realm': 'global'
|
'new_belongs_realm': 'global'
|
||||||
else if (_editingChannel != null && _belongToRealm?.id != _editingChannel?.realm?.id)
|
else if (_editingChannel != null &&
|
||||||
|
_belongToRealm?.id != _editingChannel?.realm?.id)
|
||||||
'new_belongs_realm': _belongToRealm!.alias,
|
'new_belongs_realm': _belongToRealm!.alias,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -139,8 +141,11 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: widget.editingChannelAlias != null ? Text('screenChatManage').tr() : Text('screenChatNew').tr(),
|
title: widget.editingChannelAlias != null
|
||||||
|
? Text('screenChatManage').tr()
|
||||||
|
: Text('screenChatNew').tr(),
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -152,7 +157,8 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
|
|||||||
leadingPadding: const EdgeInsets.only(left: 10, right: 20),
|
leadingPadding: const EdgeInsets.only(left: 10, right: 20),
|
||||||
dividerColor: Colors.transparent,
|
dividerColor: Colors.transparent,
|
||||||
content: Text(
|
content: Text(
|
||||||
'channelEditingNotice'.tr(args: ['#${_editingChannel!.alias}']),
|
'channelEditingNotice'
|
||||||
|
.tr(args: ['#${_editingChannel!.alias}']),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
@@ -192,12 +198,15 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(item.name).textStyle(Theme.of(context).textTheme.bodyMedium!),
|
Text(item.name).textStyle(Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!),
|
||||||
Text(
|
Text(
|
||||||
item.description,
|
item.description,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
).textStyle(Theme.of(context).textTheme.bodySmall!),
|
).textStyle(
|
||||||
|
Theme.of(context).textTheme.bodySmall!),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -213,7 +222,8 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
|
|||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
radius: 16,
|
radius: 16,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
foregroundColor: Theme.of(context).colorScheme.onSurface,
|
foregroundColor:
|
||||||
|
Theme.of(context).colorScheme.onSurface,
|
||||||
child: const Icon(Symbols.clear),
|
child: const Icon(Symbols.clear),
|
||||||
),
|
),
|
||||||
const Gap(12),
|
const Gap(12),
|
||||||
@@ -222,7 +232,9 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('fieldChatBelongToRealmUnset').tr().textStyle(
|
Text('fieldChatBelongToRealmUnset')
|
||||||
|
.tr()
|
||||||
|
.textStyle(
|
||||||
Theme.of(context).textTheme.bodyMedium!,
|
Theme.of(context).textTheme.bodyMedium!,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -257,7 +269,8 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
|
|||||||
helperText: 'fieldChatAliasHint'.tr(),
|
helperText: 'fieldChatAliasHint'.tr(),
|
||||||
helperMaxLines: 2,
|
helperMaxLines: 2,
|
||||||
),
|
),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const Gap(4),
|
const Gap(4),
|
||||||
TextField(
|
TextField(
|
||||||
@@ -266,7 +279,8 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
|
|||||||
border: const UnderlineInputBorder(),
|
border: const UnderlineInputBorder(),
|
||||||
labelText: 'fieldChatName'.tr(),
|
labelText: 'fieldChatName'.tr(),
|
||||||
),
|
),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const Gap(4),
|
const Gap(4),
|
||||||
TextField(
|
TextField(
|
||||||
@@ -277,7 +291,8 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
|
|||||||
border: const UnderlineInputBorder(),
|
border: const UnderlineInputBorder(),
|
||||||
labelText: 'fieldChatDescription'.tr(),
|
labelText: 'fieldChatDescription'.tr(),
|
||||||
),
|
),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const Gap(12),
|
const Gap(12),
|
||||||
CheckboxListTile(
|
CheckboxListTile(
|
||||||
|
@@ -304,6 +304,7 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
|
|||||||
final ud = context.read<UserDirectoryProvider>();
|
final ud = context.read<UserDirectoryProvider>();
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
_channel?.type == 1
|
_channel?.type == 1
|
||||||
|
@@ -157,6 +157,7 @@ class _ExploreScreenState extends State<ExploreScreen>
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final cfg = context.watch<ConfigProvider>();
|
final cfg = context.watch<ConfigProvider>();
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
floatingActionButtonLocation: ExpandableFab.location,
|
floatingActionButtonLocation: ExpandableFab.location,
|
||||||
floatingActionButton: ExpandableFab(
|
floatingActionButton: ExpandableFab(
|
||||||
key: _fabKey,
|
key: _fabKey,
|
||||||
@@ -243,6 +244,8 @@ class _ExploreScreenState extends State<ExploreScreen>
|
|||||||
GoRouter.of(context).pushNamed('postShuffle');
|
GoRouter.of(context).pushNamed('postShuffle');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
if (ResponsiveBreakpoints.of(context).largerThan(MOBILE))
|
||||||
|
const Gap(48),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
@@ -534,6 +537,7 @@ class _PostListWidgetState extends State<_PostListWidget> {
|
|||||||
switch (ele.type) {
|
switch (ele.type) {
|
||||||
case 'interactive.post':
|
case 'interactive.post':
|
||||||
return OpenablePostItem(
|
return OpenablePostItem(
|
||||||
|
useReplace: true,
|
||||||
data: SnPost.fromJson(ele.data),
|
data: SnPost.fromJson(ele.data),
|
||||||
maxWidth: 640,
|
maxWidth: 640,
|
||||||
onChanged: (data) {
|
onChanged: (data) {
|
||||||
|
@@ -10,7 +10,6 @@ import 'package:surface/providers/userinfo.dart';
|
|||||||
import 'package:surface/types/account.dart';
|
import 'package:surface/types/account.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
import 'package:surface/widgets/account/account_select.dart';
|
import 'package:surface/widgets/account/account_select.dart';
|
||||||
import 'package:surface/widgets/app_bar_leading.dart';
|
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/loading_indicator.dart';
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||||
@@ -46,7 +45,8 @@ class _FriendScreenState extends State<FriendScreen> {
|
|||||||
try {
|
try {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
final resp = await sn.client.get('/cgi/id/users/me/relations?status=1');
|
final resp = await sn.client.get('/cgi/id/users/me/relations?status=1');
|
||||||
_relations = List<SnRelationship>.from(resp.data?.map((e) => SnRelationship.fromJson(e)) ?? []);
|
_relations = List<SnRelationship>.from(
|
||||||
|
resp.data?.map((e) => SnRelationship.fromJson(e)) ?? []);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@@ -64,7 +64,8 @@ class _FriendScreenState extends State<FriendScreen> {
|
|||||||
try {
|
try {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
final resp = await sn.client.get('/cgi/id/users/me/relations?status=0,3');
|
final resp = await sn.client.get('/cgi/id/users/me/relations?status=0,3');
|
||||||
_requests = List<SnRelationship>.from(resp.data?.map((e) => SnRelationship.fromJson(e)) ?? []);
|
_requests = List<SnRelationship>.from(
|
||||||
|
resp.data?.map((e) => SnRelationship.fromJson(e)) ?? []);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@@ -82,7 +83,8 @@ class _FriendScreenState extends State<FriendScreen> {
|
|||||||
try {
|
try {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
final resp = await sn.client.get('/cgi/id/users/me/relations?status=2');
|
final resp = await sn.client.get('/cgi/id/users/me/relations?status=2');
|
||||||
_blocks = List<SnRelationship>.from(resp.data?.map((e) => SnRelationship.fromJson(e)) ?? []);
|
_blocks = List<SnRelationship>.from(
|
||||||
|
resp.data?.map((e) => SnRelationship.fromJson(e)) ?? []);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@@ -98,7 +100,8 @@ class _FriendScreenState extends State<FriendScreen> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final rel = context.read<SnRelationshipProvider>();
|
final rel = context.read<SnRelationshipProvider>();
|
||||||
await rel.updateRelationship(relation.relatedId, dstStatus, relation.permNodes);
|
await rel.updateRelationship(
|
||||||
|
relation.relatedId, dstStatus, relation.permNodes);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
_fetchRelations();
|
_fetchRelations();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -112,7 +115,8 @@ class _FriendScreenState extends State<FriendScreen> {
|
|||||||
Future<void> _deleteRelation(SnRelationship relation) async {
|
Future<void> _deleteRelation(SnRelationship relation) async {
|
||||||
final confirm = await context.showConfirmDialog(
|
final confirm = await context.showConfirmDialog(
|
||||||
'friendDelete'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]),
|
'friendDelete'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]),
|
||||||
'friendDeleteDescription'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]),
|
'friendDeleteDescription'
|
||||||
|
.tr(args: [relation.related?.nick ?? 'unknown'.tr()]),
|
||||||
);
|
);
|
||||||
if (!confirm) return;
|
if (!confirm) return;
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
@@ -133,7 +137,10 @@ class _FriendScreenState extends State<FriendScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showRequests() {
|
void _showRequests() {
|
||||||
showModalBottomSheet(context: context, builder: (context) => _FriendshipListWidget(relations: _requests)).then((
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => _FriendshipListWidget(relations: _requests))
|
||||||
|
.then((
|
||||||
value,
|
value,
|
||||||
) {
|
) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
@@ -144,7 +151,9 @@ class _FriendScreenState extends State<FriendScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showBlocks() {
|
void _showBlocks() {
|
||||||
showModalBottomSheet(context: context, builder: (context) => _FriendshipListWidget(relations: _blocks)).then((
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => _FriendshipListWidget(relations: _blocks)).then((
|
||||||
value,
|
value,
|
||||||
) {
|
) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
@@ -159,7 +168,8 @@ class _FriendScreenState extends State<FriendScreen> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
await sn.client.post('/cgi/id/users/me/relations', data: {'related': user.name});
|
await sn.client
|
||||||
|
.post('/cgi/id/users/me/relations', data: {'related': user.name});
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showSnackbar('friendRequestSent'.tr());
|
context.showSnackbar('friendRequestSent'.tr());
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -184,13 +194,19 @@ class _FriendScreenState extends State<FriendScreen> {
|
|||||||
|
|
||||||
if (!ua.isAuthorized) {
|
if (!ua.isAuthorized) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
appBar: AppBar(leading: PageBackButton(), title: Text('screenFriend').tr()),
|
appBar: AppBar(
|
||||||
|
leading: PageBackButton(),
|
||||||
|
title: Text('screenFriend').tr(),
|
||||||
|
),
|
||||||
body: Center(child: UnauthorizedHint()),
|
body: Center(child: UnauthorizedHint()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
appBar: AppBar(leading: AutoAppBarLeading(), title: Text('screenFriend').tr()),
|
appBar: AppBar(
|
||||||
|
leading: PageBackButton(),
|
||||||
|
title: Text('screenFriend').tr(),
|
||||||
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
child: const Icon(Symbols.add),
|
child: const Icon(Symbols.add),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
@@ -209,7 +225,8 @@ class _FriendScreenState extends State<FriendScreen> {
|
|||||||
if (_requests.isNotEmpty)
|
if (_requests.isNotEmpty)
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text('friendRequests').tr(),
|
title: Text('friendRequests').tr(),
|
||||||
subtitle: Text('friendRequestsDescription').plural(_requests.length),
|
subtitle:
|
||||||
|
Text('friendRequestsDescription').plural(_requests.length),
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
leading: const Icon(Symbols.group_add),
|
leading: const Icon(Symbols.group_add),
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
@@ -218,19 +235,22 @@ class _FriendScreenState extends State<FriendScreen> {
|
|||||||
if (_blocks.isNotEmpty)
|
if (_blocks.isNotEmpty)
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text('friendBlocklist').tr(),
|
title: Text('friendBlocklist').tr(),
|
||||||
subtitle: Text('friendBlocklistDescription').plural(_blocks.length),
|
subtitle:
|
||||||
|
Text('friendBlocklistDescription').plural(_blocks.length),
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
leading: const Icon(Symbols.block),
|
leading: const Icon(Symbols.block),
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
onTap: _showBlocks,
|
onTap: _showBlocks,
|
||||||
),
|
),
|
||||||
if (_requests.isNotEmpty || _blocks.isNotEmpty) const Divider(height: 1),
|
if (_requests.isNotEmpty || _blocks.isNotEmpty)
|
||||||
|
const Divider(height: 1),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: MediaQuery.removePadding(
|
child: MediaQuery.removePadding(
|
||||||
context: context,
|
context: context,
|
||||||
removeTop: true,
|
removeTop: true,
|
||||||
child: RefreshIndicator(
|
child: RefreshIndicator(
|
||||||
onRefresh: () => Future.wait([_fetchRelations(), _fetchRequests()]),
|
onRefresh: () =>
|
||||||
|
Future.wait([_fetchRelations(), _fetchRequests()]),
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
itemCount: _relations.length,
|
itemCount: _relations.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
@@ -254,12 +274,16 @@ class _FriendScreenState extends State<FriendScreen> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: _isUpdating ? null : () => _changeRelation(relation, 2),
|
onTap: _isUpdating
|
||||||
|
? null
|
||||||
|
: () => _changeRelation(relation, 2),
|
||||||
child: Text('friendBlock').tr(),
|
child: Text('friendBlock').tr(),
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: _isUpdating ? null : () => _deleteRelation(relation),
|
onTap: _isUpdating
|
||||||
|
? null
|
||||||
|
: () => _deleteRelation(relation),
|
||||||
child: Text('friendDeleteAction').tr(),
|
child: Text('friendDeleteAction').tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -328,7 +352,8 @@ class _FriendshipListWidgetState extends State<_FriendshipListWidget> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final rel = context.read<SnRelationshipProvider>();
|
final rel = context.read<SnRelationshipProvider>();
|
||||||
await rel.updateRelationship(relation.relatedId, dstStatus, relation.permNodes);
|
await rel.updateRelationship(
|
||||||
|
relation.relatedId, dstStatus, relation.permNodes);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
Navigator.pop(context, true);
|
Navigator.pop(context, true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -342,7 +367,8 @@ class _FriendshipListWidgetState extends State<_FriendshipListWidget> {
|
|||||||
Future<void> _deleteRelation(SnRelationship relation) async {
|
Future<void> _deleteRelation(SnRelationship relation) async {
|
||||||
final confirm = await context.showConfirmDialog(
|
final confirm = await context.showConfirmDialog(
|
||||||
'friendDelete'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]),
|
'friendDelete'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]),
|
||||||
'friendDeleteDescription'.tr(args: [relation.related?.nick ?? 'unknown'.tr()]),
|
'friendDeleteDescription'
|
||||||
|
.tr(args: [relation.related?.nick ?? 'unknown'.tr()]),
|
||||||
);
|
);
|
||||||
if (!confirm) return;
|
if (!confirm) return;
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
@@ -382,7 +408,9 @@ class _FriendshipListWidgetState extends State<_FriendshipListWidget> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
Text(kFriendStatus[relation.status] ?? 'unknown').tr().opacity(0.75),
|
Text(kFriendStatus[relation.status] ?? 'unknown')
|
||||||
|
.tr()
|
||||||
|
.opacity(0.75),
|
||||||
if (relation.status == 0)
|
if (relation.status == 0)
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
@@ -403,7 +431,8 @@ class _FriendshipListWidgetState extends State<_FriendshipListWidget> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: _isBusy ? null : () => _changeRelation(relation, 1),
|
onTap:
|
||||||
|
_isBusy ? null : () => _changeRelation(relation, 1),
|
||||||
child: Text('friendUnblock').tr(),
|
child: Text('friendUnblock').tr(),
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
|
@@ -396,35 +396,44 @@ class _HomeDashServiceStatusState extends State<_HomeDashServiceStatus> {
|
|||||||
: switch (_serviceStatus) {
|
: switch (_serviceStatus) {
|
||||||
ServiceStatus.operational => Row(
|
ServiceStatus.operational => Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
Icon(
|
||||||
Symbols.check,
|
Symbols.check,
|
||||||
size: 20,
|
size: 20,
|
||||||
|
color: Colors.green[900],
|
||||||
),
|
),
|
||||||
const Gap(10),
|
const Gap(10),
|
||||||
Text('serviceStatusOperational').tr(),
|
Text('serviceStatusOperational')
|
||||||
|
.tr()
|
||||||
|
.textColor(Colors.green[900]),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
ServiceStatus.failed => Tooltip(
|
ServiceStatus.failed => Tooltip(
|
||||||
message: 'serviceStatusFailedDescription'.tr(),
|
message: 'serviceStatusFailedDescription'.tr(),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
Icon(
|
||||||
Symbols.dangerous,
|
Symbols.dangerous,
|
||||||
size: 20,
|
size: 20,
|
||||||
|
color: Colors.red[900],
|
||||||
),
|
),
|
||||||
const Gap(10),
|
const Gap(10),
|
||||||
Text('serviceStatusFailed').tr(),
|
Text('serviceStatusFailed')
|
||||||
|
.tr()
|
||||||
|
.textColor(Colors.red[900]),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_ => Row(
|
_ => Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
Icon(
|
||||||
Symbols.error,
|
Symbols.error,
|
||||||
size: 20,
|
size: 20,
|
||||||
|
color: Colors.orange[900],
|
||||||
),
|
),
|
||||||
const Gap(10),
|
const Gap(10),
|
||||||
Text('serviceStatusDowngraded').tr(),
|
Text('serviceStatusDowngraded')
|
||||||
|
.tr()
|
||||||
|
.textColor(Colors.orange[900]),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
@@ -11,13 +11,11 @@ import 'package:styled_widget/styled_widget.dart';
|
|||||||
import 'package:surface/providers/notification.dart';
|
import 'package:surface/providers/notification.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/types/notification.dart';
|
import 'package:surface/types/notification.dart';
|
||||||
import 'package:surface/types/post.dart';
|
|
||||||
import 'package:surface/widgets/app_bar_leading.dart';
|
import 'package:surface/widgets/app_bar_leading.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/loading_indicator.dart';
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
import 'package:surface/widgets/markdown_content.dart';
|
import 'package:surface/widgets/markdown_content.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||||
import 'package:surface/widgets/post/post_item.dart';
|
|
||||||
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
|
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
|
||||||
|
|
||||||
import '../providers/userinfo.dart';
|
import '../providers/userinfo.dart';
|
||||||
@@ -219,34 +217,24 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
'interactive.subscription',
|
'interactive.subscription',
|
||||||
].contains(nty.topic) &&
|
].contains(nty.topic) &&
|
||||||
nty.metadata['related_post'] != null)
|
nty.metadata['related_post'] != null)
|
||||||
GestureDetector(
|
TextButton(
|
||||||
child: Container(
|
style: ButtonStyle(
|
||||||
decoration: BoxDecoration(
|
padding: WidgetStatePropertyAll(
|
||||||
borderRadius: const BorderRadius.all(
|
EdgeInsets.zero,
|
||||||
Radius.circular(8)),
|
|
||||||
border: Border.all(
|
|
||||||
color: Theme.of(context).dividerColor,
|
|
||||||
width: 1),
|
|
||||||
),
|
),
|
||||||
child: PostItem(
|
visualDensity: VisualDensity.compact,
|
||||||
data: SnPost.fromJson(
|
|
||||||
nty.metadata['related_post']!),
|
|
||||||
showComments: false,
|
|
||||||
showReactions: false,
|
|
||||||
showMenu: false,
|
|
||||||
).padding(vertical: 4),
|
|
||||||
),
|
),
|
||||||
onTap: () {
|
child: Text('postReadMore').tr(),
|
||||||
|
onPressed: () {
|
||||||
GoRouter.of(context).pushNamed(
|
GoRouter.of(context).pushNamed(
|
||||||
'postDetail',
|
'postDetail',
|
||||||
pathParameters: {
|
pathParameters: {
|
||||||
'slug': nty
|
'slug': nty.metadata['related_post']['id']
|
||||||
.metadata['related_post']!['id']
|
.toString(),
|
||||||
.toString()
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).padding(top: 8),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
|
@@ -12,7 +12,6 @@ import 'package:surface/providers/userinfo.dart';
|
|||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/loading_indicator.dart';
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
import 'package:surface/widgets/navigation/app_background.dart';
|
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||||
import 'package:surface/widgets/post/post_comment_list.dart';
|
import 'package:surface/widgets/post/post_comment_list.dart';
|
||||||
import 'package:surface/widgets/post/post_item.dart';
|
import 'package:surface/widgets/post/post_item.dart';
|
||||||
@@ -66,9 +65,8 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
|
|
||||||
final double maxWidth = _data?.type == 'video' ? double.infinity : 640;
|
final double maxWidth = _data?.type == 'video' ? double.infinity : 640;
|
||||||
|
|
||||||
return AppBackground(
|
return AppScaffold(
|
||||||
isRoot: widget.onBack != null,
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
child: AppScaffold(
|
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: BackButton(
|
leading: BackButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -89,16 +87,14 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
TextSpan(
|
TextSpan(
|
||||||
text: _data?.body['title'] ?? 'postNoun'.tr(),
|
text: _data?.body['title'] ?? 'postNoun'.tr(),
|
||||||
style: Theme.of(context).textTheme.titleLarge!.copyWith(
|
style: Theme.of(context).textTheme.titleLarge!.copyWith(
|
||||||
color:
|
color: Theme.of(context).appBarTheme.foregroundColor!,
|
||||||
Theme.of(context).appBarTheme.foregroundColor!,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const TextSpan(text: '\n'),
|
const TextSpan(text: '\n'),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: 'postDetail'.tr(),
|
text: 'postDetail'.tr(),
|
||||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
color:
|
color: Theme.of(context).appBarTheme.foregroundColor!,
|
||||||
Theme.of(context).appBarTheme.foregroundColor!,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
@@ -175,7 +171,6 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
SliverGap(math.max(MediaQuery.of(context).padding.bottom, 16)),
|
SliverGap(math.max(MediaQuery.of(context).padding.bottom, 16)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -79,6 +79,7 @@ class _PostShuffleScreenState extends State<PostShuffleScreen> {
|
|||||||
key: ValueKey(ele),
|
key: ValueKey(ele),
|
||||||
data: ele,
|
data: ele,
|
||||||
maxWidth: 640,
|
maxWidth: 640,
|
||||||
|
useReplace: true,
|
||||||
onChanged: (ele) {
|
onChanged: (ele) {
|
||||||
_posts[idx] = ele;
|
_posts[idx] = ele;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
@@ -38,7 +38,7 @@ class _PostPublisherScreenState extends State<PostPublisherScreen>
|
|||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
late final ScrollController _scrollController = ScrollController();
|
late final ScrollController _scrollController = ScrollController();
|
||||||
late final TabController _tabController =
|
late final TabController _tabController =
|
||||||
TabController(length: 3, vsync: this);
|
TabController(length: 5, vsync: this);
|
||||||
|
|
||||||
SnPublisher? _publisher;
|
SnPublisher? _publisher;
|
||||||
SnAccount? _account;
|
SnAccount? _account;
|
||||||
@@ -137,7 +137,7 @@ class _PostPublisherScreenState extends State<PostPublisherScreen>
|
|||||||
|
|
||||||
late final _appBarWidth = MediaQuery.of(context).size.width;
|
late final _appBarWidth = MediaQuery.of(context).size.width;
|
||||||
late final _appBarHeight =
|
late final _appBarHeight =
|
||||||
(_appBarWidth * kBannerAspectRatio).roundToDouble();
|
math.min((_appBarWidth * kBannerAspectRatio), 360).roundToDouble();
|
||||||
|
|
||||||
void _updateAppBarBlur() {
|
void _updateAppBarBlur() {
|
||||||
if (_scrollController.offset > _appBarHeight) return;
|
if (_scrollController.offset > _appBarHeight) return;
|
||||||
@@ -165,6 +165,8 @@ class _PostPublisherScreenState extends State<PostPublisherScreen>
|
|||||||
type: switch (_tabController.index) {
|
type: switch (_tabController.index) {
|
||||||
1 => 'story',
|
1 => 'story',
|
||||||
2 => 'article',
|
2 => 'article',
|
||||||
|
3 => 'question',
|
||||||
|
4 => 'video',
|
||||||
_ => null,
|
_ => null,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -284,6 +286,7 @@ class _PostPublisherScreenState extends State<PostPublisherScreen>
|
|||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
body: NestedScrollView(
|
body: NestedScrollView(
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
|
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
|
||||||
@@ -568,6 +571,18 @@ class _PostPublisherScreenState extends State<PostPublisherScreen>
|
|||||||
color: Theme.of(context).colorScheme.onSurface,
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Tab(
|
||||||
|
icon: Icon(
|
||||||
|
Symbols.help,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Tab(
|
||||||
|
icon: Icon(
|
||||||
|
Symbols.video_call,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(child: const Divider(height: 1)),
|
SliverToBoxAdapter(child: const Divider(height: 1)),
|
||||||
|
@@ -80,6 +80,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
final dt = context.read<DatabaseProvider>();
|
final dt = context.read<DatabaseProvider>();
|
||||||
|
final cfg = context.watch<ConfigProvider>();
|
||||||
|
|
||||||
|
final now = DateTime.now();
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
@@ -322,20 +325,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
CheckboxListTile(
|
|
||||||
secondary: const Icon(Symbols.left_panel_close),
|
|
||||||
title: Text('settingsDrawerPreferCollapse').tr(),
|
|
||||||
subtitle:
|
|
||||||
Text('settingsDrawerPreferCollapseDescription').tr(),
|
|
||||||
contentPadding: const EdgeInsets.only(left: 24, right: 17),
|
|
||||||
value: _prefs.getBool(kAppDrawerPreferCollapse) ?? false,
|
|
||||||
onChanged: (value) {
|
|
||||||
_prefs.setBool(kAppDrawerPreferCollapse, value ?? false);
|
|
||||||
final cfg = context.read<ConfigProvider>();
|
|
||||||
cfg.calcDrawerSize(context);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
CheckboxListTile(
|
CheckboxListTile(
|
||||||
secondary: const Icon(Symbols.hide),
|
secondary: const Icon(Symbols.hide),
|
||||||
title: Text('settingsHideBottomNav').tr(),
|
title: Text('settingsHideBottomNav').tr(),
|
||||||
@@ -349,6 +338,31 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
CheckboxListTile(
|
||||||
|
value: cfg.soundEffects,
|
||||||
|
onChanged: (value) {
|
||||||
|
cfg.soundEffects = value ?? false;
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
contentPadding: const EdgeInsets.only(left: 24, right: 17),
|
||||||
|
title: Text('settingsSoundEffects').tr(),
|
||||||
|
subtitle: Text('settingsSoundEffectsDescription').tr(),
|
||||||
|
secondary: const Icon(Symbols.sound_sampler),
|
||||||
|
),
|
||||||
|
if (!kIsWeb && !(Platform.isAndroid || Platform.isIOS))
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Symbols.window),
|
||||||
|
title: Text('settingsResetMemorizedWindowSize').tr(),
|
||||||
|
subtitle:
|
||||||
|
Text('settingsResetMemorizedWindowSizeDescription')
|
||||||
|
.tr(),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
contentPadding: const EdgeInsets.only(left: 24, right: 24),
|
||||||
|
onTap: () {
|
||||||
|
final prefs = context.read<ConfigProvider>().prefs;
|
||||||
|
prefs.remove(kAppWindowSize);
|
||||||
|
},
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Symbols.font_download),
|
leading: const Icon(Symbols.font_download),
|
||||||
title: Text('settingsCustomFonts').tr(),
|
title: Text('settingsCustomFonts').tr(),
|
||||||
@@ -741,6 +755,18 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
GoRouter.of(context).pushNamed('about');
|
GoRouter.of(context).pushNamed('about');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
if (now.day == 1 && now.month == 4)
|
||||||
|
CheckboxListTile(
|
||||||
|
title: Text('settingsAprilFoolFeatures').tr(),
|
||||||
|
subtitle: Text('settingsAprilFoolFeaturesDescription').tr(),
|
||||||
|
contentPadding: const EdgeInsets.only(left: 24, right: 17),
|
||||||
|
secondary: const Icon(Symbols.new_releases),
|
||||||
|
value: cfg.aprilFoolFeatures,
|
||||||
|
onChanged: (value) {
|
||||||
|
cfg.aprilFoolFeatures = value ?? false;
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@@ -50,6 +50,7 @@ class _AppSharingListenerState extends State<AppSharingListener> {
|
|||||||
Card(
|
Card(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
const SizedBox(width: double.infinity),
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding:
|
contentPadding:
|
||||||
const EdgeInsets.symmetric(horizontal: 24),
|
const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
@@ -45,7 +45,9 @@ class _WalletScreenState extends State<WalletScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
appBar: AppBar(leading: PageBackButton(), title: Text('screenAccountWallet').tr()),
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
|
appBar: AppBar(
|
||||||
|
leading: PageBackButton(), title: Text('screenAccountWallet').tr()),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
LoadingIndicator(isActive: _isBusy),
|
LoadingIndicator(isActive: _isBusy),
|
||||||
@@ -66,7 +68,9 @@ class _WalletScreenState extends State<WalletScreen> {
|
|||||||
SizedBox(width: double.infinity),
|
SizedBox(width: double.infinity),
|
||||||
Text(
|
Text(
|
||||||
NumberFormat.compactCurrency(
|
NumberFormat.compactCurrency(
|
||||||
locale: EasyLocalization.of(context)!.currentLocale.toString(),
|
locale: EasyLocalization.of(context)!
|
||||||
|
.currentLocale
|
||||||
|
.toString(),
|
||||||
symbol: '${'walletCurrencyShort'.tr()} ',
|
symbol: '${'walletCurrencyShort'.tr()} ',
|
||||||
decimalDigits: 2,
|
decimalDigits: 2,
|
||||||
).format(double.parse(_wallet!.balance)),
|
).format(double.parse(_wallet!.balance)),
|
||||||
@@ -76,17 +80,21 @@ class _WalletScreenState extends State<WalletScreen> {
|
|||||||
const Gap(16),
|
const Gap(16),
|
||||||
Text(
|
Text(
|
||||||
NumberFormat.compactCurrency(
|
NumberFormat.compactCurrency(
|
||||||
locale: EasyLocalization.of(context)!.currentLocale.toString(),
|
locale: EasyLocalization.of(context)!
|
||||||
|
.currentLocale
|
||||||
|
.toString(),
|
||||||
symbol: '${'walletCurrencyGoldenShort'.tr()} ',
|
symbol: '${'walletCurrencyGoldenShort'.tr()} ',
|
||||||
decimalDigits: 2,
|
decimalDigits: 2,
|
||||||
).format(double.parse(_wallet!.goldenBalance)),
|
).format(double.parse(_wallet!.goldenBalance)),
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
Text('walletCurrencyGolden'.plural(double.parse(_wallet!.goldenBalance))),
|
Text('walletCurrencyGolden'
|
||||||
|
.plural(double.parse(_wallet!.goldenBalance))),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 20, vertical: 24),
|
).padding(horizontal: 20, vertical: 24),
|
||||||
).padding(horizontal: 8, top: 16, bottom: 4),
|
).padding(horizontal: 8, top: 16, bottom: 4),
|
||||||
if (_wallet != null) Expanded(child: _WalletTransactionList(myself: _wallet!)),
|
if (_wallet != null)
|
||||||
|
Expanded(child: _WalletTransactionList(myself: _wallet!)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -116,7 +124,10 @@ class _WalletTransactionListState extends State<_WalletTransactionList> {
|
|||||||
queryParameters: {'take': 10, 'offset': _transactions.length},
|
queryParameters: {'take': 10, 'offset': _transactions.length},
|
||||||
);
|
);
|
||||||
_totalCount = resp.data['count'];
|
_totalCount = resp.data['count'];
|
||||||
_transactions.addAll(resp.data['data']?.map((e) => SnTransaction.fromJson(e)).cast<SnTransaction>() ?? []);
|
_transactions.addAll(resp.data['data']
|
||||||
|
?.map((e) => SnTransaction.fromJson(e))
|
||||||
|
.cast<SnTransaction>() ??
|
||||||
|
[]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@@ -141,7 +152,8 @@ class _WalletTransactionListState extends State<_WalletTransactionList> {
|
|||||||
child: InfiniteList(
|
child: InfiniteList(
|
||||||
itemCount: _transactions.length,
|
itemCount: _transactions.length,
|
||||||
isLoading: _isBusy,
|
isLoading: _isBusy,
|
||||||
hasReachedMax: _totalCount != null && _transactions.length >= _totalCount!,
|
hasReachedMax:
|
||||||
|
_totalCount != null && _transactions.length >= _totalCount!,
|
||||||
onFetchData: () {
|
onFetchData: () {
|
||||||
_fetchTransactions();
|
_fetchTransactions();
|
||||||
},
|
},
|
||||||
@@ -149,7 +161,9 @@ class _WalletTransactionListState extends State<_WalletTransactionList> {
|
|||||||
final ele = _transactions[idx];
|
final ele = _transactions[idx];
|
||||||
final isIncoming = ele.payeeId == widget.myself.id;
|
final isIncoming = ele.payeeId == widget.myself.id;
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: isIncoming ? const Icon(Symbols.call_received) : const Icon(Symbols.call_made),
|
leading: isIncoming
|
||||||
|
? const Icon(Symbols.call_received)
|
||||||
|
: const Icon(Symbols.call_made),
|
||||||
title: Text(
|
title: Text(
|
||||||
'${isIncoming ? '+' : '-'}${ele.amount} ${'walletCurrencyShort'.tr()}',
|
'${isIncoming ? '+' : '-'}${ele.amount} ${'walletCurrencyShort'.tr()}',
|
||||||
style: TextStyle(color: isIncoming ? Colors.green : Colors.red),
|
style: TextStyle(color: isIncoming ? Colors.green : Colors.red),
|
||||||
@@ -162,12 +176,20 @@ class _WalletTransactionListState extends State<_WalletTransactionList> {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'walletTransactionType${ele.currency.capitalize()}'.tr(),
|
'walletTransactionType${ele.currency.capitalize()}'
|
||||||
|
.tr(),
|
||||||
style: Theme.of(context).textTheme.labelSmall,
|
style: Theme.of(context).textTheme.labelSmall,
|
||||||
),
|
),
|
||||||
Text(' · ').textStyle(Theme.of(context).textTheme.labelSmall!).padding(right: 4),
|
Text(' · ')
|
||||||
|
.textStyle(Theme.of(context).textTheme.labelSmall!)
|
||||||
|
.padding(right: 4),
|
||||||
Text(
|
Text(
|
||||||
DateFormat(null, EasyLocalization.of(context)!.currentLocale.toString()).format(ele.createdAt),
|
DateFormat(
|
||||||
|
null,
|
||||||
|
EasyLocalization.of(context)!
|
||||||
|
.currentLocale
|
||||||
|
.toString())
|
||||||
|
.format(ele.createdAt),
|
||||||
style: Theme.of(context).textTheme.labelSmall,
|
style: Theme.of(context).textTheme.labelSmall,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -199,8 +221,7 @@ class _CreateWalletWidgetState extends State<_CreateWalletWidget> {
|
|||||||
final TextEditingController passwordController = TextEditingController();
|
final TextEditingController passwordController = TextEditingController();
|
||||||
final password = await showDialog<String?>(
|
final password = await showDialog<String?>(
|
||||||
context: context,
|
context: context,
|
||||||
builder:
|
builder: (ctx) => AlertDialog(
|
||||||
(ctx) => AlertDialog(
|
|
||||||
title: Text('walletCreate').tr(),
|
title: Text('walletCreate').tr(),
|
||||||
content: Column(
|
content: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -217,7 +238,9 @@ class _CreateWalletWidgetState extends State<_CreateWalletWidget> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: () => Navigator.of(ctx).pop(), child: Text('cancel').tr()),
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(ctx).pop(),
|
||||||
|
child: Text('cancel').tr()),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(ctx).pop(passwordController.text);
|
Navigator.of(ctx).pop(passwordController.text);
|
||||||
@@ -257,12 +280,18 @@ class _CreateWalletWidgetState extends State<_CreateWalletWidget> {
|
|||||||
children: [
|
children: [
|
||||||
CircleAvatar(radius: 28, child: Icon(Symbols.add, size: 28)),
|
CircleAvatar(radius: 28, child: Icon(Symbols.add, size: 28)),
|
||||||
const Gap(12),
|
const Gap(12),
|
||||||
Text('walletCreate', style: Theme.of(context).textTheme.titleLarge).tr(),
|
Text('walletCreate',
|
||||||
Text('walletCreateSubtitle', style: Theme.of(context).textTheme.bodyMedium).tr(),
|
style: Theme.of(context).textTheme.titleLarge)
|
||||||
|
.tr(),
|
||||||
|
Text('walletCreateSubtitle',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium)
|
||||||
|
.tr(),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: TextButton(onPressed: _isBusy ? null : () => _createWallet(), child: Text('next').tr()),
|
child: TextButton(
|
||||||
|
onPressed: _isBusy ? null : () => _createWallet(),
|
||||||
|
child: Text('next').tr()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 20, vertical: 24),
|
).padding(horizontal: 20, vertical: 24),
|
||||||
|
@@ -223,3 +223,24 @@ abstract class SnProgramMember with _$SnProgramMember {
|
|||||||
factory SnProgramMember.fromJson(Map<String, Object?> json) =>
|
factory SnProgramMember.fromJson(Map<String, Object?> json) =>
|
||||||
_$SnProgramMemberFromJson(json);
|
_$SnProgramMemberFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
abstract class SnPunishment with _$SnPunishment {
|
||||||
|
const factory SnPunishment({
|
||||||
|
required int id,
|
||||||
|
required DateTime createdAt,
|
||||||
|
required DateTime updatedAt,
|
||||||
|
required DateTime? deletedAt,
|
||||||
|
required String reason,
|
||||||
|
required int type,
|
||||||
|
@Default({}) Map<String, dynamic> permNodes,
|
||||||
|
required DateTime? expiredAt,
|
||||||
|
required SnAccount? account,
|
||||||
|
required int? accountId,
|
||||||
|
required SnAccount? moderator,
|
||||||
|
required int? moderatorId,
|
||||||
|
}) = _SnPunishment;
|
||||||
|
|
||||||
|
factory SnPunishment.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$SnPunishmentFromJson(json);
|
||||||
|
}
|
||||||
|
@@ -4229,4 +4229,461 @@ class __$SnProgramMemberCopyWithImpl<$Res>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnPunishment {
|
||||||
|
int get id;
|
||||||
|
DateTime get createdAt;
|
||||||
|
DateTime get updatedAt;
|
||||||
|
DateTime? get deletedAt;
|
||||||
|
String get reason;
|
||||||
|
int get type;
|
||||||
|
Map<String, dynamic> get permNodes;
|
||||||
|
DateTime? get expiredAt;
|
||||||
|
SnAccount? get account;
|
||||||
|
int? get accountId;
|
||||||
|
SnAccount? get moderator;
|
||||||
|
int? get moderatorId;
|
||||||
|
|
||||||
|
/// Create a copy of SnPunishment
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnPunishmentCopyWith<SnPunishment> get copyWith =>
|
||||||
|
_$SnPunishmentCopyWithImpl<SnPunishment>(
|
||||||
|
this as SnPunishment, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnPunishment to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is SnPunishment &&
|
||||||
|
(identical(other.id, id) || other.id == id) &&
|
||||||
|
(identical(other.createdAt, createdAt) ||
|
||||||
|
other.createdAt == createdAt) &&
|
||||||
|
(identical(other.updatedAt, updatedAt) ||
|
||||||
|
other.updatedAt == updatedAt) &&
|
||||||
|
(identical(other.deletedAt, deletedAt) ||
|
||||||
|
other.deletedAt == deletedAt) &&
|
||||||
|
(identical(other.reason, reason) || other.reason == reason) &&
|
||||||
|
(identical(other.type, type) || other.type == type) &&
|
||||||
|
const DeepCollectionEquality().equals(other.permNodes, permNodes) &&
|
||||||
|
(identical(other.expiredAt, expiredAt) ||
|
||||||
|
other.expiredAt == expiredAt) &&
|
||||||
|
(identical(other.account, account) || other.account == account) &&
|
||||||
|
(identical(other.accountId, accountId) ||
|
||||||
|
other.accountId == accountId) &&
|
||||||
|
(identical(other.moderator, moderator) ||
|
||||||
|
other.moderator == moderator) &&
|
||||||
|
(identical(other.moderatorId, moderatorId) ||
|
||||||
|
other.moderatorId == moderatorId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
id,
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
deletedAt,
|
||||||
|
reason,
|
||||||
|
type,
|
||||||
|
const DeepCollectionEquality().hash(permNodes),
|
||||||
|
expiredAt,
|
||||||
|
account,
|
||||||
|
accountId,
|
||||||
|
moderator,
|
||||||
|
moderatorId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnPunishment(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, reason: $reason, type: $type, permNodes: $permNodes, expiredAt: $expiredAt, account: $account, accountId: $accountId, moderator: $moderator, moderatorId: $moderatorId)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $SnPunishmentCopyWith<$Res> {
|
||||||
|
factory $SnPunishmentCopyWith(
|
||||||
|
SnPunishment value, $Res Function(SnPunishment) _then) =
|
||||||
|
_$SnPunishmentCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{int id,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt,
|
||||||
|
DateTime? deletedAt,
|
||||||
|
String reason,
|
||||||
|
int type,
|
||||||
|
Map<String, dynamic> permNodes,
|
||||||
|
DateTime? expiredAt,
|
||||||
|
SnAccount? account,
|
||||||
|
int? accountId,
|
||||||
|
SnAccount? moderator,
|
||||||
|
int? moderatorId});
|
||||||
|
|
||||||
|
$SnAccountCopyWith<$Res>? get account;
|
||||||
|
$SnAccountCopyWith<$Res>? get moderator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnPunishmentCopyWithImpl<$Res> implements $SnPunishmentCopyWith<$Res> {
|
||||||
|
_$SnPunishmentCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnPunishment _self;
|
||||||
|
final $Res Function(SnPunishment) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnPunishment
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = null,
|
||||||
|
Object? createdAt = null,
|
||||||
|
Object? updatedAt = null,
|
||||||
|
Object? deletedAt = freezed,
|
||||||
|
Object? reason = null,
|
||||||
|
Object? type = null,
|
||||||
|
Object? permNodes = null,
|
||||||
|
Object? expiredAt = freezed,
|
||||||
|
Object? account = freezed,
|
||||||
|
Object? accountId = freezed,
|
||||||
|
Object? moderator = freezed,
|
||||||
|
Object? moderatorId = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
id: null == id
|
||||||
|
? _self.id
|
||||||
|
: id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
createdAt: null == createdAt
|
||||||
|
? _self.createdAt
|
||||||
|
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
updatedAt: null == updatedAt
|
||||||
|
? _self.updatedAt
|
||||||
|
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
deletedAt: freezed == deletedAt
|
||||||
|
? _self.deletedAt
|
||||||
|
: deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
reason: null == reason
|
||||||
|
? _self.reason
|
||||||
|
: reason // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
type: null == type
|
||||||
|
? _self.type
|
||||||
|
: type // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
permNodes: null == permNodes
|
||||||
|
? _self.permNodes
|
||||||
|
: permNodes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, dynamic>,
|
||||||
|
expiredAt: freezed == expiredAt
|
||||||
|
? _self.expiredAt
|
||||||
|
: expiredAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
account: freezed == account
|
||||||
|
? _self.account
|
||||||
|
: account // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnAccount?,
|
||||||
|
accountId: freezed == accountId
|
||||||
|
? _self.accountId
|
||||||
|
: accountId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int?,
|
||||||
|
moderator: freezed == moderator
|
||||||
|
? _self.moderator
|
||||||
|
: moderator // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnAccount?,
|
||||||
|
moderatorId: freezed == moderatorId
|
||||||
|
? _self.moderatorId
|
||||||
|
: moderatorId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnPunishment
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnAccountCopyWith<$Res>? get account {
|
||||||
|
if (_self.account == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnAccountCopyWith<$Res>(_self.account!, (value) {
|
||||||
|
return _then(_self.copyWith(account: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnPunishment
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnAccountCopyWith<$Res>? get moderator {
|
||||||
|
if (_self.moderator == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnAccountCopyWith<$Res>(_self.moderator!, (value) {
|
||||||
|
return _then(_self.copyWith(moderator: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _SnPunishment implements SnPunishment {
|
||||||
|
const _SnPunishment(
|
||||||
|
{required this.id,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.deletedAt,
|
||||||
|
required this.reason,
|
||||||
|
required this.type,
|
||||||
|
final Map<String, dynamic> permNodes = const {},
|
||||||
|
required this.expiredAt,
|
||||||
|
required this.account,
|
||||||
|
required this.accountId,
|
||||||
|
required this.moderator,
|
||||||
|
required this.moderatorId})
|
||||||
|
: _permNodes = permNodes;
|
||||||
|
factory _SnPunishment.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnPunishmentFromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int id;
|
||||||
|
@override
|
||||||
|
final DateTime createdAt;
|
||||||
|
@override
|
||||||
|
final DateTime updatedAt;
|
||||||
|
@override
|
||||||
|
final DateTime? deletedAt;
|
||||||
|
@override
|
||||||
|
final String reason;
|
||||||
|
@override
|
||||||
|
final int type;
|
||||||
|
final Map<String, dynamic> _permNodes;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
Map<String, dynamic> get permNodes {
|
||||||
|
if (_permNodes is EqualUnmodifiableMapView) return _permNodes;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_permNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final DateTime? expiredAt;
|
||||||
|
@override
|
||||||
|
final SnAccount? account;
|
||||||
|
@override
|
||||||
|
final int? accountId;
|
||||||
|
@override
|
||||||
|
final SnAccount? moderator;
|
||||||
|
@override
|
||||||
|
final int? moderatorId;
|
||||||
|
|
||||||
|
/// Create a copy of SnPunishment
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnPunishmentCopyWith<_SnPunishment> get copyWith =>
|
||||||
|
__$SnPunishmentCopyWithImpl<_SnPunishment>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnPunishmentToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _SnPunishment &&
|
||||||
|
(identical(other.id, id) || other.id == id) &&
|
||||||
|
(identical(other.createdAt, createdAt) ||
|
||||||
|
other.createdAt == createdAt) &&
|
||||||
|
(identical(other.updatedAt, updatedAt) ||
|
||||||
|
other.updatedAt == updatedAt) &&
|
||||||
|
(identical(other.deletedAt, deletedAt) ||
|
||||||
|
other.deletedAt == deletedAt) &&
|
||||||
|
(identical(other.reason, reason) || other.reason == reason) &&
|
||||||
|
(identical(other.type, type) || other.type == type) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._permNodes, _permNodes) &&
|
||||||
|
(identical(other.expiredAt, expiredAt) ||
|
||||||
|
other.expiredAt == expiredAt) &&
|
||||||
|
(identical(other.account, account) || other.account == account) &&
|
||||||
|
(identical(other.accountId, accountId) ||
|
||||||
|
other.accountId == accountId) &&
|
||||||
|
(identical(other.moderator, moderator) ||
|
||||||
|
other.moderator == moderator) &&
|
||||||
|
(identical(other.moderatorId, moderatorId) ||
|
||||||
|
other.moderatorId == moderatorId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
id,
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
deletedAt,
|
||||||
|
reason,
|
||||||
|
type,
|
||||||
|
const DeepCollectionEquality().hash(_permNodes),
|
||||||
|
expiredAt,
|
||||||
|
account,
|
||||||
|
accountId,
|
||||||
|
moderator,
|
||||||
|
moderatorId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnPunishment(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, reason: $reason, type: $type, permNodes: $permNodes, expiredAt: $expiredAt, account: $account, accountId: $accountId, moderator: $moderator, moderatorId: $moderatorId)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnPunishmentCopyWith<$Res>
|
||||||
|
implements $SnPunishmentCopyWith<$Res> {
|
||||||
|
factory _$SnPunishmentCopyWith(
|
||||||
|
_SnPunishment value, $Res Function(_SnPunishment) _then) =
|
||||||
|
__$SnPunishmentCopyWithImpl;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{int id,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt,
|
||||||
|
DateTime? deletedAt,
|
||||||
|
String reason,
|
||||||
|
int type,
|
||||||
|
Map<String, dynamic> permNodes,
|
||||||
|
DateTime? expiredAt,
|
||||||
|
SnAccount? account,
|
||||||
|
int? accountId,
|
||||||
|
SnAccount? moderator,
|
||||||
|
int? moderatorId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
$SnAccountCopyWith<$Res>? get account;
|
||||||
|
@override
|
||||||
|
$SnAccountCopyWith<$Res>? get moderator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnPunishmentCopyWithImpl<$Res>
|
||||||
|
implements _$SnPunishmentCopyWith<$Res> {
|
||||||
|
__$SnPunishmentCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnPunishment _self;
|
||||||
|
final $Res Function(_SnPunishment) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnPunishment
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$Res call({
|
||||||
|
Object? id = null,
|
||||||
|
Object? createdAt = null,
|
||||||
|
Object? updatedAt = null,
|
||||||
|
Object? deletedAt = freezed,
|
||||||
|
Object? reason = null,
|
||||||
|
Object? type = null,
|
||||||
|
Object? permNodes = null,
|
||||||
|
Object? expiredAt = freezed,
|
||||||
|
Object? account = freezed,
|
||||||
|
Object? accountId = freezed,
|
||||||
|
Object? moderator = freezed,
|
||||||
|
Object? moderatorId = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_SnPunishment(
|
||||||
|
id: null == id
|
||||||
|
? _self.id
|
||||||
|
: id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
createdAt: null == createdAt
|
||||||
|
? _self.createdAt
|
||||||
|
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
updatedAt: null == updatedAt
|
||||||
|
? _self.updatedAt
|
||||||
|
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
deletedAt: freezed == deletedAt
|
||||||
|
? _self.deletedAt
|
||||||
|
: deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
reason: null == reason
|
||||||
|
? _self.reason
|
||||||
|
: reason // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
type: null == type
|
||||||
|
? _self.type
|
||||||
|
: type // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
permNodes: null == permNodes
|
||||||
|
? _self._permNodes
|
||||||
|
: permNodes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, dynamic>,
|
||||||
|
expiredAt: freezed == expiredAt
|
||||||
|
? _self.expiredAt
|
||||||
|
: expiredAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
account: freezed == account
|
||||||
|
? _self.account
|
||||||
|
: account // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnAccount?,
|
||||||
|
accountId: freezed == accountId
|
||||||
|
? _self.accountId
|
||||||
|
: accountId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int?,
|
||||||
|
moderator: freezed == moderator
|
||||||
|
? _self.moderator
|
||||||
|
: moderator // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnAccount?,
|
||||||
|
moderatorId: freezed == moderatorId
|
||||||
|
? _self.moderatorId
|
||||||
|
: moderatorId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnPunishment
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnAccountCopyWith<$Res>? get account {
|
||||||
|
if (_self.account == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnAccountCopyWith<$Res>(_self.account!, (value) {
|
||||||
|
return _then(_self.copyWith(account: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnPunishment
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnAccountCopyWith<$Res>? get moderator {
|
||||||
|
if (_self.moderator == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnAccountCopyWith<$Res>(_self.moderator!, (value) {
|
||||||
|
return _then(_self.copyWith(moderator: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// dart format on
|
// dart format on
|
||||||
|
@@ -380,3 +380,43 @@ Map<String, dynamic> _$SnProgramMemberToJson(_SnProgramMember instance) =>
|
|||||||
'program': instance.program.toJson(),
|
'program': instance.program.toJson(),
|
||||||
'program_id': instance.programId,
|
'program_id': instance.programId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_SnPunishment _$SnPunishmentFromJson(Map<String, dynamic> json) =>
|
||||||
|
_SnPunishment(
|
||||||
|
id: (json['id'] as num).toInt(),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt: json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
reason: json['reason'] as String,
|
||||||
|
type: (json['type'] as num).toInt(),
|
||||||
|
permNodes: json['perm_nodes'] as Map<String, dynamic>? ?? const {},
|
||||||
|
expiredAt: json['expired_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['expired_at'] as String),
|
||||||
|
account: json['account'] == null
|
||||||
|
? null
|
||||||
|
: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
|
||||||
|
accountId: (json['account_id'] as num?)?.toInt(),
|
||||||
|
moderator: json['moderator'] == null
|
||||||
|
? null
|
||||||
|
: SnAccount.fromJson(json['moderator'] as Map<String, dynamic>),
|
||||||
|
moderatorId: (json['moderator_id'] as num?)?.toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnPunishmentToJson(_SnPunishment instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'reason': instance.reason,
|
||||||
|
'type': instance.type,
|
||||||
|
'perm_nodes': instance.permNodes,
|
||||||
|
'expired_at': instance.expiredAt?.toIso8601String(),
|
||||||
|
'account': instance.account?.toJson(),
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
'moderator': instance.moderator?.toJson(),
|
||||||
|
'moderator_id': instance.moderatorId,
|
||||||
|
};
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:surface/types/account.dart';
|
||||||
|
|
||||||
part 'attachment.freezed.dart';
|
part 'attachment.freezed.dart';
|
||||||
|
|
||||||
@@ -39,6 +40,7 @@ abstract class SnAttachment with _$SnAttachment {
|
|||||||
required int? refId,
|
required int? refId,
|
||||||
required SnAttachmentPool? pool,
|
required SnAttachmentPool? pool,
|
||||||
required int? poolId,
|
required int? poolId,
|
||||||
|
required SnAccount? account,
|
||||||
required int accountId,
|
required int accountId,
|
||||||
int? thumbnailId,
|
int? thumbnailId,
|
||||||
SnAttachment? thumbnail,
|
SnAttachment? thumbnail,
|
||||||
@@ -49,7 +51,8 @@ abstract class SnAttachment with _$SnAttachment {
|
|||||||
@Default({}) Map<String, dynamic> metadata,
|
@Default({}) Map<String, dynamic> metadata,
|
||||||
}) = _SnAttachment;
|
}) = _SnAttachment;
|
||||||
|
|
||||||
factory SnAttachment.fromJson(Map<String, Object?> json) => _$SnAttachmentFromJson(json);
|
factory SnAttachment.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$SnAttachmentFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> get data => {
|
Map<String, dynamic> get data => {
|
||||||
...metadata,
|
...metadata,
|
||||||
@@ -85,7 +88,8 @@ abstract class SnAttachmentFragment with _$SnAttachmentFragment {
|
|||||||
@Default([]) List<String> fileChunksMissing,
|
@Default([]) List<String> fileChunksMissing,
|
||||||
}) = _SnAttachmentFragment;
|
}) = _SnAttachmentFragment;
|
||||||
|
|
||||||
factory SnAttachmentFragment.fromJson(Map<String, Object?> json) => _$SnAttachmentFragmentFromJson(json);
|
factory SnAttachmentFragment.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$SnAttachmentFragmentFromJson(json);
|
||||||
|
|
||||||
SnMediaType get mediaType => switch (mimetype.split('/').firstOrNull) {
|
SnMediaType get mediaType => switch (mimetype.split('/').firstOrNull) {
|
||||||
'image' => SnMediaType.image,
|
'image' => SnMediaType.image,
|
||||||
@@ -109,7 +113,8 @@ abstract class SnAttachmentPool with _$SnAttachmentPool {
|
|||||||
required int? accountId,
|
required int? accountId,
|
||||||
}) = _SnAttachmentPool;
|
}) = _SnAttachmentPool;
|
||||||
|
|
||||||
factory SnAttachmentPool.fromJson(Map<String, Object?> json) => _$SnAttachmentPoolFromJson(json);
|
factory SnAttachmentPool.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$SnAttachmentPoolFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
@@ -122,7 +127,8 @@ abstract class SnAttachmentDestination with _$SnAttachmentDestination {
|
|||||||
required bool isBoost,
|
required bool isBoost,
|
||||||
}) = _SnAttachmentDestination;
|
}) = _SnAttachmentDestination;
|
||||||
|
|
||||||
factory SnAttachmentDestination.fromJson(Map<String, Object?> json) => _$SnAttachmentDestinationFromJson(json);
|
factory SnAttachmentDestination.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$SnAttachmentDestinationFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
@@ -139,7 +145,8 @@ abstract class SnAttachmentBoost with _$SnAttachmentBoost {
|
|||||||
required int account,
|
required int account,
|
||||||
}) = _SnAttachmentBoost;
|
}) = _SnAttachmentBoost;
|
||||||
|
|
||||||
factory SnAttachmentBoost.fromJson(Map<String, Object?> json) => _$SnAttachmentBoostFromJson(json);
|
factory SnAttachmentBoost.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$SnAttachmentBoostFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
@@ -158,7 +165,8 @@ abstract class SnSticker with _$SnSticker {
|
|||||||
required int accountId,
|
required int accountId,
|
||||||
}) = _SnSticker;
|
}) = _SnSticker;
|
||||||
|
|
||||||
factory SnSticker.fromJson(Map<String, Object?> json) => _$SnStickerFromJson(json);
|
factory SnSticker.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$SnStickerFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
@@ -175,7 +183,8 @@ abstract class SnStickerPack with _$SnStickerPack {
|
|||||||
required int accountId,
|
required int accountId,
|
||||||
}) = _SnStickerPack;
|
}) = _SnStickerPack;
|
||||||
|
|
||||||
factory SnStickerPack.fromJson(Map<String, Object?> json) => _$SnStickerPackFromJson(json);
|
factory SnStickerPack.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$SnStickerPackFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
@@ -186,5 +195,6 @@ abstract class SnAttachmentBilling with _$SnAttachmentBilling {
|
|||||||
required double includedRatio,
|
required double includedRatio,
|
||||||
}) = _SnAttachmentBilling;
|
}) = _SnAttachmentBilling;
|
||||||
|
|
||||||
factory SnAttachmentBilling.fromJson(Map<String, Object?> json) => _$SnAttachmentBillingFromJson(json);
|
factory SnAttachmentBilling.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$SnAttachmentBillingFromJson(json);
|
||||||
}
|
}
|
||||||
|
@@ -38,6 +38,7 @@ mixin _$SnAttachment {
|
|||||||
int? get refId;
|
int? get refId;
|
||||||
SnAttachmentPool? get pool;
|
SnAttachmentPool? get pool;
|
||||||
int? get poolId;
|
int? get poolId;
|
||||||
|
SnAccount? get account;
|
||||||
int get accountId;
|
int get accountId;
|
||||||
int? get thumbnailId;
|
int? get thumbnailId;
|
||||||
SnAttachment? get thumbnail;
|
SnAttachment? get thumbnail;
|
||||||
@@ -98,6 +99,7 @@ mixin _$SnAttachment {
|
|||||||
(identical(other.refId, refId) || other.refId == refId) &&
|
(identical(other.refId, refId) || other.refId == refId) &&
|
||||||
(identical(other.pool, pool) || other.pool == pool) &&
|
(identical(other.pool, pool) || other.pool == pool) &&
|
||||||
(identical(other.poolId, poolId) || other.poolId == poolId) &&
|
(identical(other.poolId, poolId) || other.poolId == poolId) &&
|
||||||
|
(identical(other.account, account) || other.account == account) &&
|
||||||
(identical(other.accountId, accountId) ||
|
(identical(other.accountId, accountId) ||
|
||||||
other.accountId == accountId) &&
|
other.accountId == accountId) &&
|
||||||
(identical(other.thumbnailId, thumbnailId) ||
|
(identical(other.thumbnailId, thumbnailId) ||
|
||||||
@@ -140,6 +142,7 @@ mixin _$SnAttachment {
|
|||||||
refId,
|
refId,
|
||||||
pool,
|
pool,
|
||||||
poolId,
|
poolId,
|
||||||
|
account,
|
||||||
accountId,
|
accountId,
|
||||||
thumbnailId,
|
thumbnailId,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
@@ -152,7 +155,7 @@ mixin _$SnAttachment {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnAttachment(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, rid: $rid, uuid: $uuid, size: $size, name: $name, alt: $alt, mimetype: $mimetype, hash: $hash, destination: $destination, refCount: $refCount, contentRating: $contentRating, qualityRating: $qualityRating, cleanedAt: $cleanedAt, isAnalyzed: $isAnalyzed, isSelfRef: $isSelfRef, isIndexable: $isIndexable, ref: $ref, refId: $refId, pool: $pool, poolId: $poolId, accountId: $accountId, thumbnailId: $thumbnailId, thumbnail: $thumbnail, compressedId: $compressedId, compressed: $compressed, boosts: $boosts, usermeta: $usermeta, metadata: $metadata)';
|
return 'SnAttachment(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, rid: $rid, uuid: $uuid, size: $size, name: $name, alt: $alt, mimetype: $mimetype, hash: $hash, destination: $destination, refCount: $refCount, contentRating: $contentRating, qualityRating: $qualityRating, cleanedAt: $cleanedAt, isAnalyzed: $isAnalyzed, isSelfRef: $isSelfRef, isIndexable: $isIndexable, ref: $ref, refId: $refId, pool: $pool, poolId: $poolId, account: $account, accountId: $accountId, thumbnailId: $thumbnailId, thumbnail: $thumbnail, compressedId: $compressedId, compressed: $compressed, boosts: $boosts, usermeta: $usermeta, metadata: $metadata)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,6 +189,7 @@ abstract mixin class $SnAttachmentCopyWith<$Res> {
|
|||||||
int? refId,
|
int? refId,
|
||||||
SnAttachmentPool? pool,
|
SnAttachmentPool? pool,
|
||||||
int? poolId,
|
int? poolId,
|
||||||
|
SnAccount? account,
|
||||||
int accountId,
|
int accountId,
|
||||||
int? thumbnailId,
|
int? thumbnailId,
|
||||||
SnAttachment? thumbnail,
|
SnAttachment? thumbnail,
|
||||||
@@ -197,6 +201,7 @@ abstract mixin class $SnAttachmentCopyWith<$Res> {
|
|||||||
|
|
||||||
$SnAttachmentCopyWith<$Res>? get ref;
|
$SnAttachmentCopyWith<$Res>? get ref;
|
||||||
$SnAttachmentPoolCopyWith<$Res>? get pool;
|
$SnAttachmentPoolCopyWith<$Res>? get pool;
|
||||||
|
$SnAccountCopyWith<$Res>? get account;
|
||||||
$SnAttachmentCopyWith<$Res>? get thumbnail;
|
$SnAttachmentCopyWith<$Res>? get thumbnail;
|
||||||
$SnAttachmentCopyWith<$Res>? get compressed;
|
$SnAttachmentCopyWith<$Res>? get compressed;
|
||||||
}
|
}
|
||||||
@@ -236,6 +241,7 @@ class _$SnAttachmentCopyWithImpl<$Res> implements $SnAttachmentCopyWith<$Res> {
|
|||||||
Object? refId = freezed,
|
Object? refId = freezed,
|
||||||
Object? pool = freezed,
|
Object? pool = freezed,
|
||||||
Object? poolId = freezed,
|
Object? poolId = freezed,
|
||||||
|
Object? account = freezed,
|
||||||
Object? accountId = null,
|
Object? accountId = null,
|
||||||
Object? thumbnailId = freezed,
|
Object? thumbnailId = freezed,
|
||||||
Object? thumbnail = freezed,
|
Object? thumbnail = freezed,
|
||||||
@@ -338,6 +344,10 @@ class _$SnAttachmentCopyWithImpl<$Res> implements $SnAttachmentCopyWith<$Res> {
|
|||||||
? _self.poolId
|
? _self.poolId
|
||||||
: poolId // ignore: cast_nullable_to_non_nullable
|
: poolId // ignore: cast_nullable_to_non_nullable
|
||||||
as int?,
|
as int?,
|
||||||
|
account: freezed == account
|
||||||
|
? _self.account
|
||||||
|
: account // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnAccount?,
|
||||||
accountId: null == accountId
|
accountId: null == accountId
|
||||||
? _self.accountId
|
? _self.accountId
|
||||||
: accountId // ignore: cast_nullable_to_non_nullable
|
: accountId // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -401,6 +411,20 @@ class _$SnAttachmentCopyWithImpl<$Res> implements $SnAttachmentCopyWith<$Res> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnAttachment
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnAccountCopyWith<$Res>? get account {
|
||||||
|
if (_self.account == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnAccountCopyWith<$Res>(_self.account!, (value) {
|
||||||
|
return _then(_self.copyWith(account: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a copy of SnAttachment
|
/// Create a copy of SnAttachment
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@@ -457,6 +481,7 @@ class _SnAttachment extends SnAttachment {
|
|||||||
required this.refId,
|
required this.refId,
|
||||||
required this.pool,
|
required this.pool,
|
||||||
required this.poolId,
|
required this.poolId,
|
||||||
|
required this.account,
|
||||||
required this.accountId,
|
required this.accountId,
|
||||||
this.thumbnailId,
|
this.thumbnailId,
|
||||||
this.thumbnail,
|
this.thumbnail,
|
||||||
@@ -521,6 +546,8 @@ class _SnAttachment extends SnAttachment {
|
|||||||
@override
|
@override
|
||||||
final int? poolId;
|
final int? poolId;
|
||||||
@override
|
@override
|
||||||
|
final SnAccount? account;
|
||||||
|
@override
|
||||||
final int accountId;
|
final int accountId;
|
||||||
@override
|
@override
|
||||||
final int? thumbnailId;
|
final int? thumbnailId;
|
||||||
@@ -612,6 +639,7 @@ class _SnAttachment extends SnAttachment {
|
|||||||
(identical(other.refId, refId) || other.refId == refId) &&
|
(identical(other.refId, refId) || other.refId == refId) &&
|
||||||
(identical(other.pool, pool) || other.pool == pool) &&
|
(identical(other.pool, pool) || other.pool == pool) &&
|
||||||
(identical(other.poolId, poolId) || other.poolId == poolId) &&
|
(identical(other.poolId, poolId) || other.poolId == poolId) &&
|
||||||
|
(identical(other.account, account) || other.account == account) &&
|
||||||
(identical(other.accountId, accountId) ||
|
(identical(other.accountId, accountId) ||
|
||||||
other.accountId == accountId) &&
|
other.accountId == accountId) &&
|
||||||
(identical(other.thumbnailId, thumbnailId) ||
|
(identical(other.thumbnailId, thumbnailId) ||
|
||||||
@@ -654,6 +682,7 @@ class _SnAttachment extends SnAttachment {
|
|||||||
refId,
|
refId,
|
||||||
pool,
|
pool,
|
||||||
poolId,
|
poolId,
|
||||||
|
account,
|
||||||
accountId,
|
accountId,
|
||||||
thumbnailId,
|
thumbnailId,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
@@ -666,7 +695,7 @@ class _SnAttachment extends SnAttachment {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnAttachment(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, rid: $rid, uuid: $uuid, size: $size, name: $name, alt: $alt, mimetype: $mimetype, hash: $hash, destination: $destination, refCount: $refCount, contentRating: $contentRating, qualityRating: $qualityRating, cleanedAt: $cleanedAt, isAnalyzed: $isAnalyzed, isSelfRef: $isSelfRef, isIndexable: $isIndexable, ref: $ref, refId: $refId, pool: $pool, poolId: $poolId, accountId: $accountId, thumbnailId: $thumbnailId, thumbnail: $thumbnail, compressedId: $compressedId, compressed: $compressed, boosts: $boosts, usermeta: $usermeta, metadata: $metadata)';
|
return 'SnAttachment(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, rid: $rid, uuid: $uuid, size: $size, name: $name, alt: $alt, mimetype: $mimetype, hash: $hash, destination: $destination, refCount: $refCount, contentRating: $contentRating, qualityRating: $qualityRating, cleanedAt: $cleanedAt, isAnalyzed: $isAnalyzed, isSelfRef: $isSelfRef, isIndexable: $isIndexable, ref: $ref, refId: $refId, pool: $pool, poolId: $poolId, account: $account, accountId: $accountId, thumbnailId: $thumbnailId, thumbnail: $thumbnail, compressedId: $compressedId, compressed: $compressed, boosts: $boosts, usermeta: $usermeta, metadata: $metadata)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -702,6 +731,7 @@ abstract mixin class _$SnAttachmentCopyWith<$Res>
|
|||||||
int? refId,
|
int? refId,
|
||||||
SnAttachmentPool? pool,
|
SnAttachmentPool? pool,
|
||||||
int? poolId,
|
int? poolId,
|
||||||
|
SnAccount? account,
|
||||||
int accountId,
|
int accountId,
|
||||||
int? thumbnailId,
|
int? thumbnailId,
|
||||||
SnAttachment? thumbnail,
|
SnAttachment? thumbnail,
|
||||||
@@ -716,6 +746,8 @@ abstract mixin class _$SnAttachmentCopyWith<$Res>
|
|||||||
@override
|
@override
|
||||||
$SnAttachmentPoolCopyWith<$Res>? get pool;
|
$SnAttachmentPoolCopyWith<$Res>? get pool;
|
||||||
@override
|
@override
|
||||||
|
$SnAccountCopyWith<$Res>? get account;
|
||||||
|
@override
|
||||||
$SnAttachmentCopyWith<$Res>? get thumbnail;
|
$SnAttachmentCopyWith<$Res>? get thumbnail;
|
||||||
@override
|
@override
|
||||||
$SnAttachmentCopyWith<$Res>? get compressed;
|
$SnAttachmentCopyWith<$Res>? get compressed;
|
||||||
@@ -757,6 +789,7 @@ class __$SnAttachmentCopyWithImpl<$Res>
|
|||||||
Object? refId = freezed,
|
Object? refId = freezed,
|
||||||
Object? pool = freezed,
|
Object? pool = freezed,
|
||||||
Object? poolId = freezed,
|
Object? poolId = freezed,
|
||||||
|
Object? account = freezed,
|
||||||
Object? accountId = null,
|
Object? accountId = null,
|
||||||
Object? thumbnailId = freezed,
|
Object? thumbnailId = freezed,
|
||||||
Object? thumbnail = freezed,
|
Object? thumbnail = freezed,
|
||||||
@@ -859,6 +892,10 @@ class __$SnAttachmentCopyWithImpl<$Res>
|
|||||||
? _self.poolId
|
? _self.poolId
|
||||||
: poolId // ignore: cast_nullable_to_non_nullable
|
: poolId // ignore: cast_nullable_to_non_nullable
|
||||||
as int?,
|
as int?,
|
||||||
|
account: freezed == account
|
||||||
|
? _self.account
|
||||||
|
: account // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnAccount?,
|
||||||
accountId: null == accountId
|
accountId: null == accountId
|
||||||
? _self.accountId
|
? _self.accountId
|
||||||
: accountId // ignore: cast_nullable_to_non_nullable
|
: accountId // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -922,6 +959,20 @@ class __$SnAttachmentCopyWithImpl<$Res>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnAttachment
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnAccountCopyWith<$Res>? get account {
|
||||||
|
if (_self.account == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnAccountCopyWith<$Res>(_self.account!, (value) {
|
||||||
|
return _then(_self.copyWith(account: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a copy of SnAttachment
|
/// Create a copy of SnAttachment
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
|
@@ -39,6 +39,9 @@ _SnAttachment _$SnAttachmentFromJson(Map<String, dynamic> json) =>
|
|||||||
? null
|
? null
|
||||||
: SnAttachmentPool.fromJson(json['pool'] as Map<String, dynamic>),
|
: SnAttachmentPool.fromJson(json['pool'] as Map<String, dynamic>),
|
||||||
poolId: (json['pool_id'] as num?)?.toInt(),
|
poolId: (json['pool_id'] as num?)?.toInt(),
|
||||||
|
account: json['account'] == null
|
||||||
|
? null
|
||||||
|
: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
|
||||||
accountId: (json['account_id'] as num).toInt(),
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
thumbnailId: (json['thumbnail_id'] as num?)?.toInt(),
|
thumbnailId: (json['thumbnail_id'] as num?)?.toInt(),
|
||||||
thumbnail: json['thumbnail'] == null
|
thumbnail: json['thumbnail'] == null
|
||||||
@@ -82,6 +85,7 @@ Map<String, dynamic> _$SnAttachmentToJson(_SnAttachment instance) =>
|
|||||||
'ref_id': instance.refId,
|
'ref_id': instance.refId,
|
||||||
'pool': instance.pool?.toJson(),
|
'pool': instance.pool?.toJson(),
|
||||||
'pool_id': instance.poolId,
|
'pool_id': instance.poolId,
|
||||||
|
'account': instance.account?.toJson(),
|
||||||
'account_id': instance.accountId,
|
'account_id': instance.accountId,
|
||||||
'thumbnail_id': instance.thumbnailId,
|
'thumbnail_id': instance.thumbnailId,
|
||||||
'thumbnail': instance.thumbnail?.toJson(),
|
'thumbnail': instance.thumbnail?.toJson(),
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:surface/types/account.dart';
|
||||||
import 'package:surface/types/attachment.dart';
|
import 'package:surface/types/attachment.dart';
|
||||||
import 'package:surface/types/poll.dart';
|
import 'package:surface/types/poll.dart';
|
||||||
import 'package:surface/types/realm.dart';
|
import 'package:surface/types/realm.dart';
|
||||||
@@ -26,6 +27,7 @@ abstract class SnPost with _$SnPost {
|
|||||||
required int? replyId,
|
required int? replyId,
|
||||||
required int? repostId,
|
required int? repostId,
|
||||||
required int? realmId,
|
required int? realmId,
|
||||||
|
required SnRealm? realm,
|
||||||
required SnPost? replyTo,
|
required SnPost? replyTo,
|
||||||
required SnPost? repostTo,
|
required SnPost? repostTo,
|
||||||
required List<int>? visibleUsersList,
|
required List<int>? visibleUsersList,
|
||||||
@@ -43,9 +45,9 @@ abstract class SnPost with _$SnPost {
|
|||||||
@Default(0) int totalAggregatedViews,
|
@Default(0) int totalAggregatedViews,
|
||||||
required int publisherId,
|
required int publisherId,
|
||||||
required int? pollId,
|
required int? pollId,
|
||||||
|
required SnPoll? poll,
|
||||||
required SnPublisher publisher,
|
required SnPublisher publisher,
|
||||||
required SnMetric metric,
|
required SnMetric metric,
|
||||||
SnPostPreload? preload,
|
|
||||||
}) = _SnPost;
|
}) = _SnPost;
|
||||||
|
|
||||||
factory SnPost.fromJson(Map<String, Object?> json) => _$SnPostFromJson(json);
|
factory SnPost.fromJson(Map<String, Object?> json) => _$SnPostFromJson(json);
|
||||||
@@ -146,6 +148,7 @@ abstract class SnPublisher with _$SnPublisher {
|
|||||||
required int totalDownvote,
|
required int totalDownvote,
|
||||||
required int? realmId,
|
required int? realmId,
|
||||||
required int accountId,
|
required int accountId,
|
||||||
|
required SnAccount? account,
|
||||||
}) = _SnPublisher;
|
}) = _SnPublisher;
|
||||||
|
|
||||||
factory SnPublisher.fromJson(Map<String, Object?> json) =>
|
factory SnPublisher.fromJson(Map<String, Object?> json) =>
|
||||||
|
@@ -30,6 +30,7 @@ mixin _$SnPost {
|
|||||||
int? get replyId;
|
int? get replyId;
|
||||||
int? get repostId;
|
int? get repostId;
|
||||||
int? get realmId;
|
int? get realmId;
|
||||||
|
SnRealm? get realm;
|
||||||
SnPost? get replyTo;
|
SnPost? get replyTo;
|
||||||
SnPost? get repostTo;
|
SnPost? get repostTo;
|
||||||
List<int>? get visibleUsersList;
|
List<int>? get visibleUsersList;
|
||||||
@@ -47,9 +48,9 @@ mixin _$SnPost {
|
|||||||
int get totalAggregatedViews;
|
int get totalAggregatedViews;
|
||||||
int get publisherId;
|
int get publisherId;
|
||||||
int? get pollId;
|
int? get pollId;
|
||||||
|
SnPoll? get poll;
|
||||||
SnPublisher get publisher;
|
SnPublisher get publisher;
|
||||||
SnMetric get metric;
|
SnMetric get metric;
|
||||||
SnPostPreload? get preload;
|
|
||||||
|
|
||||||
/// Create a copy of SnPost
|
/// Create a copy of SnPost
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -88,6 +89,7 @@ mixin _$SnPost {
|
|||||||
(identical(other.repostId, repostId) ||
|
(identical(other.repostId, repostId) ||
|
||||||
other.repostId == repostId) &&
|
other.repostId == repostId) &&
|
||||||
(identical(other.realmId, realmId) || other.realmId == realmId) &&
|
(identical(other.realmId, realmId) || other.realmId == realmId) &&
|
||||||
|
(identical(other.realm, realm) || other.realm == realm) &&
|
||||||
(identical(other.replyTo, replyTo) || other.replyTo == replyTo) &&
|
(identical(other.replyTo, replyTo) || other.replyTo == replyTo) &&
|
||||||
(identical(other.repostTo, repostTo) ||
|
(identical(other.repostTo, repostTo) ||
|
||||||
other.repostTo == repostTo) &&
|
other.repostTo == repostTo) &&
|
||||||
@@ -119,10 +121,10 @@ mixin _$SnPost {
|
|||||||
(identical(other.publisherId, publisherId) ||
|
(identical(other.publisherId, publisherId) ||
|
||||||
other.publisherId == publisherId) &&
|
other.publisherId == publisherId) &&
|
||||||
(identical(other.pollId, pollId) || other.pollId == pollId) &&
|
(identical(other.pollId, pollId) || other.pollId == pollId) &&
|
||||||
|
(identical(other.poll, poll) || other.poll == poll) &&
|
||||||
(identical(other.publisher, publisher) ||
|
(identical(other.publisher, publisher) ||
|
||||||
other.publisher == publisher) &&
|
other.publisher == publisher) &&
|
||||||
(identical(other.metric, metric) || other.metric == metric) &&
|
(identical(other.metric, metric) || other.metric == metric));
|
||||||
(identical(other.preload, preload) || other.preload == preload));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -144,6 +146,7 @@ mixin _$SnPost {
|
|||||||
replyId,
|
replyId,
|
||||||
repostId,
|
repostId,
|
||||||
realmId,
|
realmId,
|
||||||
|
realm,
|
||||||
replyTo,
|
replyTo,
|
||||||
repostTo,
|
repostTo,
|
||||||
const DeepCollectionEquality().hash(visibleUsersList),
|
const DeepCollectionEquality().hash(visibleUsersList),
|
||||||
@@ -161,14 +164,14 @@ mixin _$SnPost {
|
|||||||
totalAggregatedViews,
|
totalAggregatedViews,
|
||||||
publisherId,
|
publisherId,
|
||||||
pollId,
|
pollId,
|
||||||
|
poll,
|
||||||
publisher,
|
publisher,
|
||||||
metric,
|
metric
|
||||||
preload
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnPost(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, body: $body, language: $language, alias: $alias, aliasPrefix: $aliasPrefix, tags: $tags, categories: $categories, replies: $replies, replyId: $replyId, repostId: $repostId, realmId: $realmId, replyTo: $replyTo, repostTo: $repostTo, visibleUsersList: $visibleUsersList, invisibleUsersList: $invisibleUsersList, visibility: $visibility, editedAt: $editedAt, pinnedAt: $pinnedAt, lockedAt: $lockedAt, isDraft: $isDraft, publishedAt: $publishedAt, publishedUntil: $publishedUntil, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, totalViews: $totalViews, totalAggregatedViews: $totalAggregatedViews, publisherId: $publisherId, pollId: $pollId, publisher: $publisher, metric: $metric, preload: $preload)';
|
return 'SnPost(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, body: $body, language: $language, alias: $alias, aliasPrefix: $aliasPrefix, tags: $tags, categories: $categories, replies: $replies, replyId: $replyId, repostId: $repostId, realmId: $realmId, realm: $realm, replyTo: $replyTo, repostTo: $repostTo, visibleUsersList: $visibleUsersList, invisibleUsersList: $invisibleUsersList, visibility: $visibility, editedAt: $editedAt, pinnedAt: $pinnedAt, lockedAt: $lockedAt, isDraft: $isDraft, publishedAt: $publishedAt, publishedUntil: $publishedUntil, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, totalViews: $totalViews, totalAggregatedViews: $totalAggregatedViews, publisherId: $publisherId, pollId: $pollId, poll: $poll, publisher: $publisher, metric: $metric)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +196,7 @@ abstract mixin class $SnPostCopyWith<$Res> {
|
|||||||
int? replyId,
|
int? replyId,
|
||||||
int? repostId,
|
int? repostId,
|
||||||
int? realmId,
|
int? realmId,
|
||||||
|
SnRealm? realm,
|
||||||
SnPost? replyTo,
|
SnPost? replyTo,
|
||||||
SnPost? repostTo,
|
SnPost? repostTo,
|
||||||
List<int>? visibleUsersList,
|
List<int>? visibleUsersList,
|
||||||
@@ -210,15 +214,16 @@ abstract mixin class $SnPostCopyWith<$Res> {
|
|||||||
int totalAggregatedViews,
|
int totalAggregatedViews,
|
||||||
int publisherId,
|
int publisherId,
|
||||||
int? pollId,
|
int? pollId,
|
||||||
|
SnPoll? poll,
|
||||||
SnPublisher publisher,
|
SnPublisher publisher,
|
||||||
SnMetric metric,
|
SnMetric metric});
|
||||||
SnPostPreload? preload});
|
|
||||||
|
|
||||||
|
$SnRealmCopyWith<$Res>? get realm;
|
||||||
$SnPostCopyWith<$Res>? get replyTo;
|
$SnPostCopyWith<$Res>? get replyTo;
|
||||||
$SnPostCopyWith<$Res>? get repostTo;
|
$SnPostCopyWith<$Res>? get repostTo;
|
||||||
|
$SnPollCopyWith<$Res>? get poll;
|
||||||
$SnPublisherCopyWith<$Res> get publisher;
|
$SnPublisherCopyWith<$Res> get publisher;
|
||||||
$SnMetricCopyWith<$Res> get metric;
|
$SnMetricCopyWith<$Res> get metric;
|
||||||
$SnPostPreloadCopyWith<$Res>? get preload;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -248,6 +253,7 @@ class _$SnPostCopyWithImpl<$Res> implements $SnPostCopyWith<$Res> {
|
|||||||
Object? replyId = freezed,
|
Object? replyId = freezed,
|
||||||
Object? repostId = freezed,
|
Object? repostId = freezed,
|
||||||
Object? realmId = freezed,
|
Object? realmId = freezed,
|
||||||
|
Object? realm = freezed,
|
||||||
Object? replyTo = freezed,
|
Object? replyTo = freezed,
|
||||||
Object? repostTo = freezed,
|
Object? repostTo = freezed,
|
||||||
Object? visibleUsersList = freezed,
|
Object? visibleUsersList = freezed,
|
||||||
@@ -265,9 +271,9 @@ class _$SnPostCopyWithImpl<$Res> implements $SnPostCopyWith<$Res> {
|
|||||||
Object? totalAggregatedViews = null,
|
Object? totalAggregatedViews = null,
|
||||||
Object? publisherId = null,
|
Object? publisherId = null,
|
||||||
Object? pollId = freezed,
|
Object? pollId = freezed,
|
||||||
|
Object? poll = freezed,
|
||||||
Object? publisher = null,
|
Object? publisher = null,
|
||||||
Object? metric = null,
|
Object? metric = null,
|
||||||
Object? preload = freezed,
|
|
||||||
}) {
|
}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
id: null == id
|
id: null == id
|
||||||
@@ -330,6 +336,10 @@ class _$SnPostCopyWithImpl<$Res> implements $SnPostCopyWith<$Res> {
|
|||||||
? _self.realmId
|
? _self.realmId
|
||||||
: realmId // ignore: cast_nullable_to_non_nullable
|
: realmId // ignore: cast_nullable_to_non_nullable
|
||||||
as int?,
|
as int?,
|
||||||
|
realm: freezed == realm
|
||||||
|
? _self.realm
|
||||||
|
: realm // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnRealm?,
|
||||||
replyTo: freezed == replyTo
|
replyTo: freezed == replyTo
|
||||||
? _self.replyTo
|
? _self.replyTo
|
||||||
: replyTo // ignore: cast_nullable_to_non_nullable
|
: replyTo // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -398,6 +408,10 @@ class _$SnPostCopyWithImpl<$Res> implements $SnPostCopyWith<$Res> {
|
|||||||
? _self.pollId
|
? _self.pollId
|
||||||
: pollId // ignore: cast_nullable_to_non_nullable
|
: pollId // ignore: cast_nullable_to_non_nullable
|
||||||
as int?,
|
as int?,
|
||||||
|
poll: freezed == poll
|
||||||
|
? _self.poll
|
||||||
|
: poll // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnPoll?,
|
||||||
publisher: null == publisher
|
publisher: null == publisher
|
||||||
? _self.publisher
|
? _self.publisher
|
||||||
: publisher // ignore: cast_nullable_to_non_nullable
|
: publisher // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -406,13 +420,23 @@ class _$SnPostCopyWithImpl<$Res> implements $SnPostCopyWith<$Res> {
|
|||||||
? _self.metric
|
? _self.metric
|
||||||
: metric // ignore: cast_nullable_to_non_nullable
|
: metric // ignore: cast_nullable_to_non_nullable
|
||||||
as SnMetric,
|
as SnMetric,
|
||||||
preload: freezed == preload
|
|
||||||
? _self.preload
|
|
||||||
: preload // ignore: cast_nullable_to_non_nullable
|
|
||||||
as SnPostPreload?,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnPost
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnRealmCopyWith<$Res>? get realm {
|
||||||
|
if (_self.realm == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnRealmCopyWith<$Res>(_self.realm!, (value) {
|
||||||
|
return _then(_self.copyWith(realm: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a copy of SnPost
|
/// Create a copy of SnPost
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@@ -441,6 +465,20 @@ class _$SnPostCopyWithImpl<$Res> implements $SnPostCopyWith<$Res> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnPost
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnPollCopyWith<$Res>? get poll {
|
||||||
|
if (_self.poll == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnPollCopyWith<$Res>(_self.poll!, (value) {
|
||||||
|
return _then(_self.copyWith(poll: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a copy of SnPost
|
/// Create a copy of SnPost
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@@ -460,20 +498,6 @@ class _$SnPostCopyWithImpl<$Res> implements $SnPostCopyWith<$Res> {
|
|||||||
return _then(_self.copyWith(metric: value));
|
return _then(_self.copyWith(metric: value));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a copy of SnPost
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$SnPostPreloadCopyWith<$Res>? get preload {
|
|
||||||
if (_self.preload == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $SnPostPreloadCopyWith<$Res>(_self.preload!, (value) {
|
|
||||||
return _then(_self.copyWith(preload: value));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -495,6 +519,7 @@ class _SnPost extends SnPost {
|
|||||||
required this.replyId,
|
required this.replyId,
|
||||||
required this.repostId,
|
required this.repostId,
|
||||||
required this.realmId,
|
required this.realmId,
|
||||||
|
required this.realm,
|
||||||
required this.replyTo,
|
required this.replyTo,
|
||||||
required this.repostTo,
|
required this.repostTo,
|
||||||
required final List<int>? visibleUsersList,
|
required final List<int>? visibleUsersList,
|
||||||
@@ -512,9 +537,9 @@ class _SnPost extends SnPost {
|
|||||||
this.totalAggregatedViews = 0,
|
this.totalAggregatedViews = 0,
|
||||||
required this.publisherId,
|
required this.publisherId,
|
||||||
required this.pollId,
|
required this.pollId,
|
||||||
|
required this.poll,
|
||||||
required this.publisher,
|
required this.publisher,
|
||||||
required this.metric,
|
required this.metric})
|
||||||
this.preload})
|
|
||||||
: _body = body,
|
: _body = body,
|
||||||
_tags = tags,
|
_tags = tags,
|
||||||
_categories = categories,
|
_categories = categories,
|
||||||
@@ -583,6 +608,8 @@ class _SnPost extends SnPost {
|
|||||||
@override
|
@override
|
||||||
final int? realmId;
|
final int? realmId;
|
||||||
@override
|
@override
|
||||||
|
final SnRealm? realm;
|
||||||
|
@override
|
||||||
final SnPost? replyTo;
|
final SnPost? replyTo;
|
||||||
@override
|
@override
|
||||||
final SnPost? repostTo;
|
final SnPost? repostTo;
|
||||||
@@ -637,11 +664,11 @@ class _SnPost extends SnPost {
|
|||||||
@override
|
@override
|
||||||
final int? pollId;
|
final int? pollId;
|
||||||
@override
|
@override
|
||||||
|
final SnPoll? poll;
|
||||||
|
@override
|
||||||
final SnPublisher publisher;
|
final SnPublisher publisher;
|
||||||
@override
|
@override
|
||||||
final SnMetric metric;
|
final SnMetric metric;
|
||||||
@override
|
|
||||||
final SnPostPreload? preload;
|
|
||||||
|
|
||||||
/// Create a copy of SnPost
|
/// Create a copy of SnPost
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -685,6 +712,7 @@ class _SnPost extends SnPost {
|
|||||||
(identical(other.repostId, repostId) ||
|
(identical(other.repostId, repostId) ||
|
||||||
other.repostId == repostId) &&
|
other.repostId == repostId) &&
|
||||||
(identical(other.realmId, realmId) || other.realmId == realmId) &&
|
(identical(other.realmId, realmId) || other.realmId == realmId) &&
|
||||||
|
(identical(other.realm, realm) || other.realm == realm) &&
|
||||||
(identical(other.replyTo, replyTo) || other.replyTo == replyTo) &&
|
(identical(other.replyTo, replyTo) || other.replyTo == replyTo) &&
|
||||||
(identical(other.repostTo, repostTo) ||
|
(identical(other.repostTo, repostTo) ||
|
||||||
other.repostTo == repostTo) &&
|
other.repostTo == repostTo) &&
|
||||||
@@ -716,10 +744,10 @@ class _SnPost extends SnPost {
|
|||||||
(identical(other.publisherId, publisherId) ||
|
(identical(other.publisherId, publisherId) ||
|
||||||
other.publisherId == publisherId) &&
|
other.publisherId == publisherId) &&
|
||||||
(identical(other.pollId, pollId) || other.pollId == pollId) &&
|
(identical(other.pollId, pollId) || other.pollId == pollId) &&
|
||||||
|
(identical(other.poll, poll) || other.poll == poll) &&
|
||||||
(identical(other.publisher, publisher) ||
|
(identical(other.publisher, publisher) ||
|
||||||
other.publisher == publisher) &&
|
other.publisher == publisher) &&
|
||||||
(identical(other.metric, metric) || other.metric == metric) &&
|
(identical(other.metric, metric) || other.metric == metric));
|
||||||
(identical(other.preload, preload) || other.preload == preload));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -741,6 +769,7 @@ class _SnPost extends SnPost {
|
|||||||
replyId,
|
replyId,
|
||||||
repostId,
|
repostId,
|
||||||
realmId,
|
realmId,
|
||||||
|
realm,
|
||||||
replyTo,
|
replyTo,
|
||||||
repostTo,
|
repostTo,
|
||||||
const DeepCollectionEquality().hash(_visibleUsersList),
|
const DeepCollectionEquality().hash(_visibleUsersList),
|
||||||
@@ -758,14 +787,14 @@ class _SnPost extends SnPost {
|
|||||||
totalAggregatedViews,
|
totalAggregatedViews,
|
||||||
publisherId,
|
publisherId,
|
||||||
pollId,
|
pollId,
|
||||||
|
poll,
|
||||||
publisher,
|
publisher,
|
||||||
metric,
|
metric
|
||||||
preload
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnPost(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, body: $body, language: $language, alias: $alias, aliasPrefix: $aliasPrefix, tags: $tags, categories: $categories, replies: $replies, replyId: $replyId, repostId: $repostId, realmId: $realmId, replyTo: $replyTo, repostTo: $repostTo, visibleUsersList: $visibleUsersList, invisibleUsersList: $invisibleUsersList, visibility: $visibility, editedAt: $editedAt, pinnedAt: $pinnedAt, lockedAt: $lockedAt, isDraft: $isDraft, publishedAt: $publishedAt, publishedUntil: $publishedUntil, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, totalViews: $totalViews, totalAggregatedViews: $totalAggregatedViews, publisherId: $publisherId, pollId: $pollId, publisher: $publisher, metric: $metric, preload: $preload)';
|
return 'SnPost(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, body: $body, language: $language, alias: $alias, aliasPrefix: $aliasPrefix, tags: $tags, categories: $categories, replies: $replies, replyId: $replyId, repostId: $repostId, realmId: $realmId, realm: $realm, replyTo: $replyTo, repostTo: $repostTo, visibleUsersList: $visibleUsersList, invisibleUsersList: $invisibleUsersList, visibility: $visibility, editedAt: $editedAt, pinnedAt: $pinnedAt, lockedAt: $lockedAt, isDraft: $isDraft, publishedAt: $publishedAt, publishedUntil: $publishedUntil, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, totalViews: $totalViews, totalAggregatedViews: $totalAggregatedViews, publisherId: $publisherId, pollId: $pollId, poll: $poll, publisher: $publisher, metric: $metric)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -791,6 +820,7 @@ abstract mixin class _$SnPostCopyWith<$Res> implements $SnPostCopyWith<$Res> {
|
|||||||
int? replyId,
|
int? replyId,
|
||||||
int? repostId,
|
int? repostId,
|
||||||
int? realmId,
|
int? realmId,
|
||||||
|
SnRealm? realm,
|
||||||
SnPost? replyTo,
|
SnPost? replyTo,
|
||||||
SnPost? repostTo,
|
SnPost? repostTo,
|
||||||
List<int>? visibleUsersList,
|
List<int>? visibleUsersList,
|
||||||
@@ -808,20 +838,22 @@ abstract mixin class _$SnPostCopyWith<$Res> implements $SnPostCopyWith<$Res> {
|
|||||||
int totalAggregatedViews,
|
int totalAggregatedViews,
|
||||||
int publisherId,
|
int publisherId,
|
||||||
int? pollId,
|
int? pollId,
|
||||||
|
SnPoll? poll,
|
||||||
SnPublisher publisher,
|
SnPublisher publisher,
|
||||||
SnMetric metric,
|
SnMetric metric});
|
||||||
SnPostPreload? preload});
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
$SnRealmCopyWith<$Res>? get realm;
|
||||||
@override
|
@override
|
||||||
$SnPostCopyWith<$Res>? get replyTo;
|
$SnPostCopyWith<$Res>? get replyTo;
|
||||||
@override
|
@override
|
||||||
$SnPostCopyWith<$Res>? get repostTo;
|
$SnPostCopyWith<$Res>? get repostTo;
|
||||||
@override
|
@override
|
||||||
|
$SnPollCopyWith<$Res>? get poll;
|
||||||
|
@override
|
||||||
$SnPublisherCopyWith<$Res> get publisher;
|
$SnPublisherCopyWith<$Res> get publisher;
|
||||||
@override
|
@override
|
||||||
$SnMetricCopyWith<$Res> get metric;
|
$SnMetricCopyWith<$Res> get metric;
|
||||||
@override
|
|
||||||
$SnPostPreloadCopyWith<$Res>? get preload;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -851,6 +883,7 @@ class __$SnPostCopyWithImpl<$Res> implements _$SnPostCopyWith<$Res> {
|
|||||||
Object? replyId = freezed,
|
Object? replyId = freezed,
|
||||||
Object? repostId = freezed,
|
Object? repostId = freezed,
|
||||||
Object? realmId = freezed,
|
Object? realmId = freezed,
|
||||||
|
Object? realm = freezed,
|
||||||
Object? replyTo = freezed,
|
Object? replyTo = freezed,
|
||||||
Object? repostTo = freezed,
|
Object? repostTo = freezed,
|
||||||
Object? visibleUsersList = freezed,
|
Object? visibleUsersList = freezed,
|
||||||
@@ -868,9 +901,9 @@ class __$SnPostCopyWithImpl<$Res> implements _$SnPostCopyWith<$Res> {
|
|||||||
Object? totalAggregatedViews = null,
|
Object? totalAggregatedViews = null,
|
||||||
Object? publisherId = null,
|
Object? publisherId = null,
|
||||||
Object? pollId = freezed,
|
Object? pollId = freezed,
|
||||||
|
Object? poll = freezed,
|
||||||
Object? publisher = null,
|
Object? publisher = null,
|
||||||
Object? metric = null,
|
Object? metric = null,
|
||||||
Object? preload = freezed,
|
|
||||||
}) {
|
}) {
|
||||||
return _then(_SnPost(
|
return _then(_SnPost(
|
||||||
id: null == id
|
id: null == id
|
||||||
@@ -933,6 +966,10 @@ class __$SnPostCopyWithImpl<$Res> implements _$SnPostCopyWith<$Res> {
|
|||||||
? _self.realmId
|
? _self.realmId
|
||||||
: realmId // ignore: cast_nullable_to_non_nullable
|
: realmId // ignore: cast_nullable_to_non_nullable
|
||||||
as int?,
|
as int?,
|
||||||
|
realm: freezed == realm
|
||||||
|
? _self.realm
|
||||||
|
: realm // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnRealm?,
|
||||||
replyTo: freezed == replyTo
|
replyTo: freezed == replyTo
|
||||||
? _self.replyTo
|
? _self.replyTo
|
||||||
: replyTo // ignore: cast_nullable_to_non_nullable
|
: replyTo // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -1001,6 +1038,10 @@ class __$SnPostCopyWithImpl<$Res> implements _$SnPostCopyWith<$Res> {
|
|||||||
? _self.pollId
|
? _self.pollId
|
||||||
: pollId // ignore: cast_nullable_to_non_nullable
|
: pollId // ignore: cast_nullable_to_non_nullable
|
||||||
as int?,
|
as int?,
|
||||||
|
poll: freezed == poll
|
||||||
|
? _self.poll
|
||||||
|
: poll // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnPoll?,
|
||||||
publisher: null == publisher
|
publisher: null == publisher
|
||||||
? _self.publisher
|
? _self.publisher
|
||||||
: publisher // ignore: cast_nullable_to_non_nullable
|
: publisher // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -1009,13 +1050,23 @@ class __$SnPostCopyWithImpl<$Res> implements _$SnPostCopyWith<$Res> {
|
|||||||
? _self.metric
|
? _self.metric
|
||||||
: metric // ignore: cast_nullable_to_non_nullable
|
: metric // ignore: cast_nullable_to_non_nullable
|
||||||
as SnMetric,
|
as SnMetric,
|
||||||
preload: freezed == preload
|
|
||||||
? _self.preload
|
|
||||||
: preload // ignore: cast_nullable_to_non_nullable
|
|
||||||
as SnPostPreload?,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnPost
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnRealmCopyWith<$Res>? get realm {
|
||||||
|
if (_self.realm == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnRealmCopyWith<$Res>(_self.realm!, (value) {
|
||||||
|
return _then(_self.copyWith(realm: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a copy of SnPost
|
/// Create a copy of SnPost
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@@ -1044,6 +1095,20 @@ class __$SnPostCopyWithImpl<$Res> implements _$SnPostCopyWith<$Res> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnPost
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnPollCopyWith<$Res>? get poll {
|
||||||
|
if (_self.poll == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnPollCopyWith<$Res>(_self.poll!, (value) {
|
||||||
|
return _then(_self.copyWith(poll: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a copy of SnPost
|
/// Create a copy of SnPost
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@@ -1063,20 +1128,6 @@ class __$SnPostCopyWithImpl<$Res> implements _$SnPostCopyWith<$Res> {
|
|||||||
return _then(_self.copyWith(metric: value));
|
return _then(_self.copyWith(metric: value));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a copy of SnPost
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$SnPostPreloadCopyWith<$Res>? get preload {
|
|
||||||
if (_self.preload == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $SnPostPreloadCopyWith<$Res>(_self.preload!, (value) {
|
|
||||||
return _then(_self.copyWith(preload: value));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -2465,6 +2516,7 @@ mixin _$SnPublisher {
|
|||||||
int get totalDownvote;
|
int get totalDownvote;
|
||||||
int? get realmId;
|
int? get realmId;
|
||||||
int get accountId;
|
int get accountId;
|
||||||
|
SnAccount? get account;
|
||||||
|
|
||||||
/// Create a copy of SnPublisher
|
/// Create a copy of SnPublisher
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -2501,7 +2553,8 @@ mixin _$SnPublisher {
|
|||||||
other.totalDownvote == totalDownvote) &&
|
other.totalDownvote == totalDownvote) &&
|
||||||
(identical(other.realmId, realmId) || other.realmId == realmId) &&
|
(identical(other.realmId, realmId) || other.realmId == realmId) &&
|
||||||
(identical(other.accountId, accountId) ||
|
(identical(other.accountId, accountId) ||
|
||||||
other.accountId == accountId));
|
other.accountId == accountId) &&
|
||||||
|
(identical(other.account, account) || other.account == account));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -2521,11 +2574,12 @@ mixin _$SnPublisher {
|
|||||||
totalUpvote,
|
totalUpvote,
|
||||||
totalDownvote,
|
totalDownvote,
|
||||||
realmId,
|
realmId,
|
||||||
accountId);
|
accountId,
|
||||||
|
account);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnPublisher(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, name: $name, nick: $nick, description: $description, avatar: $avatar, banner: $banner, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, realmId: $realmId, accountId: $accountId)';
|
return 'SnPublisher(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, name: $name, nick: $nick, description: $description, avatar: $avatar, banner: $banner, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, realmId: $realmId, accountId: $accountId, account: $account)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2549,7 +2603,10 @@ abstract mixin class $SnPublisherCopyWith<$Res> {
|
|||||||
int totalUpvote,
|
int totalUpvote,
|
||||||
int totalDownvote,
|
int totalDownvote,
|
||||||
int? realmId,
|
int? realmId,
|
||||||
int accountId});
|
int accountId,
|
||||||
|
SnAccount? account});
|
||||||
|
|
||||||
|
$SnAccountCopyWith<$Res>? get account;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -2578,6 +2635,7 @@ class _$SnPublisherCopyWithImpl<$Res> implements $SnPublisherCopyWith<$Res> {
|
|||||||
Object? totalDownvote = null,
|
Object? totalDownvote = null,
|
||||||
Object? realmId = freezed,
|
Object? realmId = freezed,
|
||||||
Object? accountId = null,
|
Object? accountId = null,
|
||||||
|
Object? account = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
id: null == id
|
id: null == id
|
||||||
@@ -2636,8 +2694,26 @@ class _$SnPublisherCopyWithImpl<$Res> implements $SnPublisherCopyWith<$Res> {
|
|||||||
? _self.accountId
|
? _self.accountId
|
||||||
: accountId // ignore: cast_nullable_to_non_nullable
|
: accountId // ignore: cast_nullable_to_non_nullable
|
||||||
as int,
|
as int,
|
||||||
|
account: freezed == account
|
||||||
|
? _self.account
|
||||||
|
: account // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnAccount?,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnPublisher
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnAccountCopyWith<$Res>? get account {
|
||||||
|
if (_self.account == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnAccountCopyWith<$Res>(_self.account!, (value) {
|
||||||
|
return _then(_self.copyWith(account: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -2657,7 +2733,8 @@ class _SnPublisher implements SnPublisher {
|
|||||||
required this.totalUpvote,
|
required this.totalUpvote,
|
||||||
required this.totalDownvote,
|
required this.totalDownvote,
|
||||||
required this.realmId,
|
required this.realmId,
|
||||||
required this.accountId});
|
required this.accountId,
|
||||||
|
required this.account});
|
||||||
factory _SnPublisher.fromJson(Map<String, dynamic> json) =>
|
factory _SnPublisher.fromJson(Map<String, dynamic> json) =>
|
||||||
_$SnPublisherFromJson(json);
|
_$SnPublisherFromJson(json);
|
||||||
|
|
||||||
@@ -2689,6 +2766,8 @@ class _SnPublisher implements SnPublisher {
|
|||||||
final int? realmId;
|
final int? realmId;
|
||||||
@override
|
@override
|
||||||
final int accountId;
|
final int accountId;
|
||||||
|
@override
|
||||||
|
final SnAccount? account;
|
||||||
|
|
||||||
/// Create a copy of SnPublisher
|
/// Create a copy of SnPublisher
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -2730,7 +2809,8 @@ class _SnPublisher implements SnPublisher {
|
|||||||
other.totalDownvote == totalDownvote) &&
|
other.totalDownvote == totalDownvote) &&
|
||||||
(identical(other.realmId, realmId) || other.realmId == realmId) &&
|
(identical(other.realmId, realmId) || other.realmId == realmId) &&
|
||||||
(identical(other.accountId, accountId) ||
|
(identical(other.accountId, accountId) ||
|
||||||
other.accountId == accountId));
|
other.accountId == accountId) &&
|
||||||
|
(identical(other.account, account) || other.account == account));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -2750,11 +2830,12 @@ class _SnPublisher implements SnPublisher {
|
|||||||
totalUpvote,
|
totalUpvote,
|
||||||
totalDownvote,
|
totalDownvote,
|
||||||
realmId,
|
realmId,
|
||||||
accountId);
|
accountId,
|
||||||
|
account);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnPublisher(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, name: $name, nick: $nick, description: $description, avatar: $avatar, banner: $banner, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, realmId: $realmId, accountId: $accountId)';
|
return 'SnPublisher(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, name: $name, nick: $nick, description: $description, avatar: $avatar, banner: $banner, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, realmId: $realmId, accountId: $accountId, account: $account)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2780,7 +2861,11 @@ abstract mixin class _$SnPublisherCopyWith<$Res>
|
|||||||
int totalUpvote,
|
int totalUpvote,
|
||||||
int totalDownvote,
|
int totalDownvote,
|
||||||
int? realmId,
|
int? realmId,
|
||||||
int accountId});
|
int accountId,
|
||||||
|
SnAccount? account});
|
||||||
|
|
||||||
|
@override
|
||||||
|
$SnAccountCopyWith<$Res>? get account;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -2809,6 +2894,7 @@ class __$SnPublisherCopyWithImpl<$Res> implements _$SnPublisherCopyWith<$Res> {
|
|||||||
Object? totalDownvote = null,
|
Object? totalDownvote = null,
|
||||||
Object? realmId = freezed,
|
Object? realmId = freezed,
|
||||||
Object? accountId = null,
|
Object? accountId = null,
|
||||||
|
Object? account = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(_SnPublisher(
|
return _then(_SnPublisher(
|
||||||
id: null == id
|
id: null == id
|
||||||
@@ -2867,8 +2953,26 @@ class __$SnPublisherCopyWithImpl<$Res> implements _$SnPublisherCopyWith<$Res> {
|
|||||||
? _self.accountId
|
? _self.accountId
|
||||||
: accountId // ignore: cast_nullable_to_non_nullable
|
: accountId // ignore: cast_nullable_to_non_nullable
|
||||||
as int,
|
as int,
|
||||||
|
account: freezed == account
|
||||||
|
? _self.account
|
||||||
|
: account // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnAccount?,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnPublisher
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnAccountCopyWith<$Res>? get account {
|
||||||
|
if (_self.account == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnAccountCopyWith<$Res>(_self.account!, (value) {
|
||||||
|
return _then(_self.copyWith(account: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
@@ -32,6 +32,9 @@ _SnPost _$SnPostFromJson(Map<String, dynamic> json) => _SnPost(
|
|||||||
replyId: (json['reply_id'] as num?)?.toInt(),
|
replyId: (json['reply_id'] as num?)?.toInt(),
|
||||||
repostId: (json['repost_id'] as num?)?.toInt(),
|
repostId: (json['repost_id'] as num?)?.toInt(),
|
||||||
realmId: (json['realm_id'] as num?)?.toInt(),
|
realmId: (json['realm_id'] as num?)?.toInt(),
|
||||||
|
realm: json['realm'] == null
|
||||||
|
? null
|
||||||
|
: SnRealm.fromJson(json['realm'] as Map<String, dynamic>),
|
||||||
replyTo: json['reply_to'] == null
|
replyTo: json['reply_to'] == null
|
||||||
? null
|
? null
|
||||||
: SnPost.fromJson(json['reply_to'] as Map<String, dynamic>),
|
: SnPost.fromJson(json['reply_to'] as Map<String, dynamic>),
|
||||||
@@ -68,12 +71,12 @@ _SnPost _$SnPostFromJson(Map<String, dynamic> json) => _SnPost(
|
|||||||
(json['total_aggregated_views'] as num?)?.toInt() ?? 0,
|
(json['total_aggregated_views'] as num?)?.toInt() ?? 0,
|
||||||
publisherId: (json['publisher_id'] as num).toInt(),
|
publisherId: (json['publisher_id'] as num).toInt(),
|
||||||
pollId: (json['poll_id'] as num?)?.toInt(),
|
pollId: (json['poll_id'] as num?)?.toInt(),
|
||||||
|
poll: json['poll'] == null
|
||||||
|
? null
|
||||||
|
: SnPoll.fromJson(json['poll'] as Map<String, dynamic>),
|
||||||
publisher:
|
publisher:
|
||||||
SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
|
SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
|
||||||
metric: SnMetric.fromJson(json['metric'] as Map<String, dynamic>),
|
metric: SnMetric.fromJson(json['metric'] as Map<String, dynamic>),
|
||||||
preload: json['preload'] == null
|
|
||||||
? null
|
|
||||||
: SnPostPreload.fromJson(json['preload'] as Map<String, dynamic>),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
|
Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
|
||||||
@@ -92,6 +95,7 @@ Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
|
|||||||
'reply_id': instance.replyId,
|
'reply_id': instance.replyId,
|
||||||
'repost_id': instance.repostId,
|
'repost_id': instance.repostId,
|
||||||
'realm_id': instance.realmId,
|
'realm_id': instance.realmId,
|
||||||
|
'realm': instance.realm?.toJson(),
|
||||||
'reply_to': instance.replyTo?.toJson(),
|
'reply_to': instance.replyTo?.toJson(),
|
||||||
'repost_to': instance.repostTo?.toJson(),
|
'repost_to': instance.repostTo?.toJson(),
|
||||||
'visible_users_list': instance.visibleUsersList,
|
'visible_users_list': instance.visibleUsersList,
|
||||||
@@ -109,9 +113,9 @@ Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
|
|||||||
'total_aggregated_views': instance.totalAggregatedViews,
|
'total_aggregated_views': instance.totalAggregatedViews,
|
||||||
'publisher_id': instance.publisherId,
|
'publisher_id': instance.publisherId,
|
||||||
'poll_id': instance.pollId,
|
'poll_id': instance.pollId,
|
||||||
|
'poll': instance.poll?.toJson(),
|
||||||
'publisher': instance.publisher.toJson(),
|
'publisher': instance.publisher.toJson(),
|
||||||
'metric': instance.metric.toJson(),
|
'metric': instance.metric.toJson(),
|
||||||
'preload': instance.preload?.toJson(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_SnPostTag _$SnPostTagFromJson(Map<String, dynamic> json) => _SnPostTag(
|
_SnPostTag _$SnPostTagFromJson(Map<String, dynamic> json) => _SnPostTag(
|
||||||
@@ -241,6 +245,9 @@ _SnPublisher _$SnPublisherFromJson(Map<String, dynamic> json) => _SnPublisher(
|
|||||||
totalDownvote: (json['total_downvote'] as num).toInt(),
|
totalDownvote: (json['total_downvote'] as num).toInt(),
|
||||||
realmId: (json['realm_id'] as num?)?.toInt(),
|
realmId: (json['realm_id'] as num?)?.toInt(),
|
||||||
accountId: (json['account_id'] as num).toInt(),
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
|
account: json['account'] == null
|
||||||
|
? null
|
||||||
|
: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$SnPublisherToJson(_SnPublisher instance) =>
|
Map<String, dynamic> _$SnPublisherToJson(_SnPublisher instance) =>
|
||||||
@@ -259,6 +266,7 @@ Map<String, dynamic> _$SnPublisherToJson(_SnPublisher instance) =>
|
|||||||
'total_downvote': instance.totalDownvote,
|
'total_downvote': instance.totalDownvote,
|
||||||
'realm_id': instance.realmId,
|
'realm_id': instance.realmId,
|
||||||
'account_id': instance.accountId,
|
'account_id': instance.accountId,
|
||||||
|
'account': instance.account?.toJson(),
|
||||||
};
|
};
|
||||||
|
|
||||||
_SnSubscription _$SnSubscriptionFromJson(Map<String, dynamic> json) =>
|
_SnSubscription _$SnSubscriptionFromJson(Map<String, dynamic> json) =>
|
||||||
|
@@ -17,4 +17,5 @@ const Map<String, ReactInfo> kTemplateReactions = {
|
|||||||
'party': ReactInfo(icon: '🎉', attitude: 1),
|
'party': ReactInfo(icon: '🎉', attitude: 1),
|
||||||
'joy': ReactInfo(icon: '🤣', attitude: 1),
|
'joy': ReactInfo(icon: '🤣', attitude: 1),
|
||||||
'pray': ReactInfo(icon: '🙏', attitude: 1),
|
'pray': ReactInfo(icon: '🙏', attitude: 1),
|
||||||
|
'heart': ReactInfo(icon: '❤️', attitude: 1),
|
||||||
};
|
};
|
||||||
|
@@ -54,11 +54,15 @@ class AccountImage extends StatelessWidget {
|
|||||||
))
|
))
|
||||||
.center(),
|
.center(),
|
||||||
)
|
)
|
||||||
: AutoResizeUniversalImage(
|
: UniversalImage(
|
||||||
sn.getAttachmentUrl(url),
|
sn.getAttachmentUrl(url),
|
||||||
filterQuality: filterQuality,
|
filterQuality: filterQuality,
|
||||||
key: Key('attachment-${content.hashCode}'),
|
key: Key('attachment-${content.hashCode}'),
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
|
width: (radius != null ? radius! : 20) * 2,
|
||||||
|
height: (radius != null ? radius! : 20) * 2,
|
||||||
|
cacheWidth: (radius != null ? radius! : 20) * 2,
|
||||||
|
cacheHeight: (radius != null ? radius! : 20) * 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@@ -5,6 +5,7 @@ import 'dart:math' as math;
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
@@ -12,6 +13,7 @@ import 'package:media_kit/media_kit.dart';
|
|||||||
import 'package:media_kit_video/media_kit_video.dart';
|
import 'package:media_kit_video/media_kit_video.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:surface/logger.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/types/attachment.dart';
|
import 'package:surface/types/attachment.dart';
|
||||||
import 'package:surface/widgets/universal_image.dart';
|
import 'package:surface/widgets/universal_image.dart';
|
||||||
@@ -222,20 +224,71 @@ class _AttachmentItemContentVideoState
|
|||||||
: sn.getAttachmentUrl(widget.data.compressed!.rid);
|
: sn.getAttachmentUrl(widget.data.compressed!.rid);
|
||||||
_videoPlayer = Player();
|
_videoPlayer = Player();
|
||||||
_videoController = VideoController(_videoPlayer!);
|
_videoController = VideoController(_videoPlayer!);
|
||||||
_videoPlayer!.open(Media(url), play: !widget.isAutoload);
|
|
||||||
|
String? uri;
|
||||||
|
final inCacheInfo = await DefaultCacheManager().getFileFromCache(url);
|
||||||
|
if (inCacheInfo == null) {
|
||||||
|
logging.info('[MediaPlayer] Miss cache: $url');
|
||||||
|
final fileStream = DefaultCacheManager().getFileStream(
|
||||||
|
url,
|
||||||
|
withProgress: true,
|
||||||
|
);
|
||||||
|
await for (var fileInfo in fileStream) {
|
||||||
|
if (fileInfo is FileInfo) {
|
||||||
|
uri = fileInfo.file.path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uri = inCacheInfo.file.path;
|
||||||
|
logging.info('[MediaPlayer] Hit cache: $url');
|
||||||
|
}
|
||||||
|
if (uri == null) {
|
||||||
|
if (mounted) {
|
||||||
|
context.showErrorDialog('attachmentFailedToLoadMedia'.tr());
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _toggleOriginal() {
|
_videoPlayer!.open(Media(uri), play: !widget.isAutoload);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _toggleOriginal() async {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
if (widget.data.compressedId == null) return;
|
if (widget.data.compressedId == null) return;
|
||||||
setState(() => _showOriginal = !_showOriginal);
|
setState(() => _showOriginal = !_showOriginal);
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
_videoPlayer?.open(
|
final url = _showOriginal
|
||||||
Media(
|
|
||||||
_showOriginal
|
|
||||||
? sn.getAttachmentUrl(widget.data.rid)
|
? sn.getAttachmentUrl(widget.data.rid)
|
||||||
: sn.getAttachmentUrl(widget.data.compressed!.rid),
|
: sn.getAttachmentUrl(widget.data.compressed!.rid);
|
||||||
),
|
|
||||||
|
String? uri;
|
||||||
|
final inCacheInfo = await DefaultCacheManager().getFileFromCache(url);
|
||||||
|
if (inCacheInfo == null) {
|
||||||
|
logging.info('[MediaPlayer] Miss cache: $url');
|
||||||
|
final fileStream = DefaultCacheManager().getFileStream(
|
||||||
|
url,
|
||||||
|
withProgress: true,
|
||||||
|
);
|
||||||
|
await for (var fileInfo in fileStream) {
|
||||||
|
if (fileInfo is FileInfo) {
|
||||||
|
uri = fileInfo.file.path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uri = inCacheInfo.file.path;
|
||||||
|
logging.info('[MediaPlayer] Hit cache: $url');
|
||||||
|
}
|
||||||
|
if (uri == null) {
|
||||||
|
if (mounted) {
|
||||||
|
context.showErrorDialog('attachmentFailedToLoadMedia'.tr());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_videoPlayer?.open(
|
||||||
|
Media(uri),
|
||||||
play: true,
|
play: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -439,7 +492,33 @@ class _AttachmentItemContentAudioState
|
|||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
final url = sn.getAttachmentUrl(widget.data.rid);
|
final url = sn.getAttachmentUrl(widget.data.rid);
|
||||||
_audioPlayer = Player();
|
_audioPlayer = Player();
|
||||||
await _audioPlayer!.open(Media(url), play: !widget.isAutoload);
|
|
||||||
|
String? uri;
|
||||||
|
final inCacheInfo = await DefaultCacheManager().getFileFromCache(url);
|
||||||
|
if (inCacheInfo == null) {
|
||||||
|
logging.info('[MediaPlayer] Miss cache: $url');
|
||||||
|
final fileStream = DefaultCacheManager().getFileStream(
|
||||||
|
url,
|
||||||
|
withProgress: true,
|
||||||
|
);
|
||||||
|
await for (var fileInfo in fileStream) {
|
||||||
|
if (fileInfo is FileInfo) {
|
||||||
|
uri = fileInfo.file.path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uri = inCacheInfo.file.path;
|
||||||
|
logging.info('[MediaPlayer] Hit cache: $url');
|
||||||
|
}
|
||||||
|
if (uri == null) {
|
||||||
|
if (mounted) {
|
||||||
|
context.showErrorDialog('attachmentFailedToLoadMedia'.tr());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _audioPlayer!.open(Media(uri), play: !widget.isAutoload);
|
||||||
_audioPlayer!.stream.playing.listen((v) => setState(() => _isPlaying = v));
|
_audioPlayer!.stream.playing.listen((v) => setState(() => _isPlaying = v));
|
||||||
_audioPlayer!.stream.position.listen((v) => setState(() => _position = v));
|
_audioPlayer!.stream.position.listen((v) => setState(() => _position = v));
|
||||||
_audioPlayer!.stream.duration.listen((v) => setState(() => _duration = v));
|
_audioPlayer!.stream.duration.listen((v) => setState(() => _duration = v));
|
||||||
@@ -567,6 +646,7 @@ class _AttachmentItemContentAudioState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
constraints: const BoxConstraints(maxWidth: 320),
|
constraints: const BoxConstraints(maxWidth: 320),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
@@ -224,8 +224,10 @@ class _AttachmentListState extends State<AttachmentList> {
|
|||||||
(widget.data[idx]?.data['ratio'] ?? 1).toDouble(),
|
(widget.data[idx]?.data['ratio'] ?? 1).toDouble(),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (widget.data[idx]?.mediaType != SnMediaType.image)
|
if (widget.data[idx]?.mediaType !=
|
||||||
|
SnMediaType.image) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
context.pushTransparentRoute(
|
context.pushTransparentRoute(
|
||||||
AttachmentZoomView(
|
AttachmentZoomView(
|
||||||
data: widget.data
|
data: widget.data
|
||||||
@@ -246,8 +248,10 @@ class _AttachmentListState extends State<AttachmentList> {
|
|||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
border:
|
border: Border.all(
|
||||||
Border(top: borderSide, bottom: borderSide),
|
width: 1,
|
||||||
|
color: Theme.of(context).dividerColor,
|
||||||
|
),
|
||||||
borderRadius: AttachmentList.kDefaultRadius,
|
borderRadius: AttachmentList.kDefaultRadius,
|
||||||
),
|
),
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
@@ -263,8 +267,8 @@ class _AttachmentListState extends State<AttachmentList> {
|
|||||||
right: 8,
|
right: 8,
|
||||||
bottom: 8,
|
bottom: 8,
|
||||||
child: Chip(
|
child: Chip(
|
||||||
label:
|
label: Text('${idx + 1}/${widget.data.length}'),
|
||||||
Text('${idx + 1}/${widget.data.length}')),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@@ -16,7 +16,6 @@ import 'package:photo_view/photo_view_gallery.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/providers/user_directory.dart';
|
|
||||||
import 'package:surface/types/attachment.dart';
|
import 'package:surface/types/attachment.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
@@ -418,8 +417,7 @@ class _AttachmentZoomDetailPopup extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ud = context.read<UserDirectoryProvider>();
|
final account = data.account!;
|
||||||
final account = ud.getFromCache(data.accountId);
|
|
||||||
|
|
||||||
const tableGap = TableRow(
|
const tableGap = TableRow(
|
||||||
children: [
|
children: [
|
||||||
@@ -461,12 +459,12 @@ class _AttachmentZoomDetailPopup extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
if (data.accountId > 0)
|
if (data.accountId > 0)
|
||||||
AccountImage(
|
AccountImage(
|
||||||
content: account?.avatar,
|
content: account.avatar,
|
||||||
radius: 8,
|
radius: 8,
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Text(data.accountId > 0
|
Text(data.accountId > 0
|
||||||
? account?.nick ?? 'unknown'.tr()
|
? account.nick
|
||||||
: 'unknown'.tr()),
|
: 'unknown'.tr()),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Text('#${data.accountId}',
|
Text('#${data.accountId}',
|
||||||
|
@@ -8,12 +8,12 @@ import 'package:surface/widgets/account/account_image.dart';
|
|||||||
class NoContentWidget extends StatefulWidget {
|
class NoContentWidget extends StatefulWidget {
|
||||||
final SnAccount? userinfo;
|
final SnAccount? userinfo;
|
||||||
final bool isSpeaking;
|
final bool isSpeaking;
|
||||||
final bool isFixed;
|
final double? avatarSize;
|
||||||
|
|
||||||
const NoContentWidget({
|
const NoContentWidget({
|
||||||
super.key,
|
super.key,
|
||||||
this.userinfo,
|
this.userinfo,
|
||||||
this.isFixed = false,
|
this.avatarSize,
|
||||||
required this.isSpeaking,
|
required this.isSpeaking,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -45,17 +45,13 @@ class _NoContentWidgetState extends State<NoContentWidget>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final double radius = widget.isFixed
|
final double radius = widget.avatarSize ??
|
||||||
? 32
|
math.min(
|
||||||
: math.min(
|
|
||||||
MediaQuery.of(context).size.width * 0.1,
|
MediaQuery.of(context).size.width * 0.1,
|
||||||
MediaQuery.of(context).size.height * 0.1,
|
MediaQuery.of(context).size.height * 0.1,
|
||||||
);
|
);
|
||||||
|
|
||||||
return Container(
|
return Animate(
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Center(
|
|
||||||
child: Animate(
|
|
||||||
autoPlay: false,
|
autoPlay: false,
|
||||||
controller: _animationController,
|
controller: _animationController,
|
||||||
effects: [
|
effects: [
|
||||||
@@ -79,8 +75,6 @@ class _NoContentWidgetState extends State<NoContentWidget>
|
|||||||
content: widget.userinfo?.avatar,
|
content: widget.userinfo?.avatar,
|
||||||
radius: radius,
|
radius: radius,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,7 +2,9 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:livekit_client/livekit_client.dart';
|
import 'package:livekit_client/livekit_client.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/types/account.dart';
|
import 'package:surface/types/account.dart';
|
||||||
import 'package:surface/types/chat.dart';
|
import 'package:surface/types/chat.dart';
|
||||||
import 'package:surface/widgets/chat/call/call_no_content.dart';
|
import 'package:surface/widgets/chat/call/call_no_content.dart';
|
||||||
@@ -11,23 +13,32 @@ import 'package:surface/widgets/chat/call/call_participant_menu.dart';
|
|||||||
import 'package:surface/widgets/chat/call/call_participant_stats.dart';
|
import 'package:surface/widgets/chat/call/call_participant_stats.dart';
|
||||||
|
|
||||||
abstract class ParticipantWidget extends StatefulWidget {
|
abstract class ParticipantWidget extends StatefulWidget {
|
||||||
static ParticipantWidget widgetFor(ParticipantTrack participantTrack,
|
static ParticipantWidget widgetFor(
|
||||||
{bool isFixed = false, bool showStatsLayer = false}) {
|
ParticipantTrack participantTrack, {
|
||||||
|
double? avatarSize,
|
||||||
|
EdgeInsets? padding,
|
||||||
|
bool showStatsLayer = false,
|
||||||
|
bool isList = false,
|
||||||
|
}) {
|
||||||
if (participantTrack.participant is LocalParticipant) {
|
if (participantTrack.participant is LocalParticipant) {
|
||||||
return LocalParticipantWidget(
|
return LocalParticipantWidget(
|
||||||
participantTrack.participant as LocalParticipant,
|
participantTrack.participant as LocalParticipant,
|
||||||
participantTrack.videoTrack,
|
participantTrack.videoTrack,
|
||||||
isFixed,
|
avatarSize,
|
||||||
participantTrack.isScreenShare,
|
participantTrack.isScreenShare,
|
||||||
showStatsLayer,
|
showStatsLayer,
|
||||||
|
isList,
|
||||||
|
padding,
|
||||||
);
|
);
|
||||||
} else if (participantTrack.participant is RemoteParticipant) {
|
} else if (participantTrack.participant is RemoteParticipant) {
|
||||||
return RemoteParticipantWidget(
|
return RemoteParticipantWidget(
|
||||||
participantTrack.participant as RemoteParticipant,
|
participantTrack.participant as RemoteParticipant,
|
||||||
participantTrack.videoTrack,
|
participantTrack.videoTrack,
|
||||||
isFixed,
|
avatarSize,
|
||||||
participantTrack.isScreenShare,
|
participantTrack.isScreenShare,
|
||||||
showStatsLayer,
|
showStatsLayer,
|
||||||
|
isList,
|
||||||
|
padding,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
throw UnimplementedError('Unknown participant type');
|
throw UnimplementedError('Unknown participant type');
|
||||||
@@ -36,8 +47,10 @@ abstract class ParticipantWidget extends StatefulWidget {
|
|||||||
abstract final Participant participant;
|
abstract final Participant participant;
|
||||||
abstract final VideoTrack? videoTrack;
|
abstract final VideoTrack? videoTrack;
|
||||||
abstract final bool isScreenShare;
|
abstract final bool isScreenShare;
|
||||||
abstract final bool isFixed;
|
abstract final double? avatarSize;
|
||||||
abstract final bool showStatsLayer;
|
abstract final bool showStatsLayer;
|
||||||
|
abstract final bool isList;
|
||||||
|
abstract final EdgeInsets? padding;
|
||||||
final VideoQuality quality;
|
final VideoQuality quality;
|
||||||
|
|
||||||
const ParticipantWidget({
|
const ParticipantWidget({
|
||||||
@@ -52,18 +65,24 @@ class LocalParticipantWidget extends ParticipantWidget {
|
|||||||
@override
|
@override
|
||||||
final VideoTrack? videoTrack;
|
final VideoTrack? videoTrack;
|
||||||
@override
|
@override
|
||||||
final bool isFixed;
|
final double? avatarSize;
|
||||||
@override
|
@override
|
||||||
final bool isScreenShare;
|
final bool isScreenShare;
|
||||||
@override
|
@override
|
||||||
final bool showStatsLayer;
|
final bool showStatsLayer;
|
||||||
|
@override
|
||||||
|
final bool isList;
|
||||||
|
@override
|
||||||
|
final EdgeInsets? padding;
|
||||||
|
|
||||||
const LocalParticipantWidget(
|
const LocalParticipantWidget(
|
||||||
this.participant,
|
this.participant,
|
||||||
this.videoTrack,
|
this.videoTrack,
|
||||||
this.isFixed,
|
this.avatarSize,
|
||||||
this.isScreenShare,
|
this.isScreenShare,
|
||||||
this.showStatsLayer, {
|
this.showStatsLayer,
|
||||||
|
this.isList,
|
||||||
|
this.padding, {
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -77,18 +96,24 @@ class RemoteParticipantWidget extends ParticipantWidget {
|
|||||||
@override
|
@override
|
||||||
final VideoTrack? videoTrack;
|
final VideoTrack? videoTrack;
|
||||||
@override
|
@override
|
||||||
final bool isFixed;
|
final double? avatarSize;
|
||||||
@override
|
@override
|
||||||
final bool isScreenShare;
|
final bool isScreenShare;
|
||||||
@override
|
@override
|
||||||
final bool showStatsLayer;
|
final bool showStatsLayer;
|
||||||
|
@override
|
||||||
|
final bool isList;
|
||||||
|
@override
|
||||||
|
final EdgeInsets? padding;
|
||||||
|
|
||||||
const RemoteParticipantWidget(
|
const RemoteParticipantWidget(
|
||||||
this.participant,
|
this.participant,
|
||||||
this.videoTrack,
|
this.videoTrack,
|
||||||
this.isFixed,
|
this.avatarSize,
|
||||||
this.isScreenShare,
|
this.isScreenShare,
|
||||||
this.showStatsLayer, {
|
this.showStatsLayer,
|
||||||
|
this.isList,
|
||||||
|
this.padding, {
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -136,19 +161,82 @@ abstract class _ParticipantWidgetState<T extends ParticipantWidget>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext ctx) {
|
Widget build(BuildContext context) {
|
||||||
|
if (widget.isList) {
|
||||||
|
return Padding(
|
||||||
|
padding: widget.padding ?? EdgeInsets.zero,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: (widget.avatarSize ?? 32) * 2,
|
||||||
|
height: (widget.avatarSize ?? 32) * 2,
|
||||||
|
child: Center(
|
||||||
|
child: NoContentWidget(
|
||||||
|
userinfo: _userinfoMetadata,
|
||||||
|
avatarSize: widget.avatarSize,
|
||||||
|
isSpeaking: widget.participant.isSpeaking,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Expanded(
|
||||||
|
child: SizedBox(
|
||||||
|
height: (widget.avatarSize ?? 32) * 2,
|
||||||
|
child: ParticipantInfoWidget(
|
||||||
|
isList: true,
|
||||||
|
title: widget.participant.name.isNotEmpty
|
||||||
|
? widget.participant.name
|
||||||
|
: widget.participant.identity,
|
||||||
|
audioAvailable: _firstAudioPublication?.muted == false &&
|
||||||
|
_firstAudioPublication?.subscribed == true,
|
||||||
|
connectionQuality: widget.participant.connectionQuality,
|
||||||
|
isScreenShare: widget.isScreenShare,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (_activeVideoTrack != null && !_activeVideoTrack!.muted)
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: 16 / 9,
|
||||||
|
child: Material(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surfaceContainer
|
||||||
|
.withOpacity(0.75),
|
||||||
|
child: VideoTrackRenderer(
|
||||||
|
_activeVideoTrack!,
|
||||||
|
fit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).padding(top: 8),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
_activeVideoTrack != null && !_activeVideoTrack!.muted
|
if (_activeVideoTrack != null && !_activeVideoTrack!.muted)
|
||||||
? VideoTrackRenderer(
|
VideoTrackRenderer(
|
||||||
_activeVideoTrack!,
|
_activeVideoTrack!,
|
||||||
fit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain,
|
fit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain,
|
||||||
)
|
)
|
||||||
: NoContentWidget(
|
else
|
||||||
|
Center(
|
||||||
|
child: NoContentWidget(
|
||||||
userinfo: _userinfoMetadata,
|
userinfo: _userinfoMetadata,
|
||||||
isFixed: widget.isFixed,
|
avatarSize: widget.avatarSize,
|
||||||
isSpeaking: widget.participant.isSpeaking,
|
isSpeaking: widget.participant.isSpeaking,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
if (widget.showStatsLayer)
|
if (widget.showStatsLayer)
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 30,
|
top: 30,
|
||||||
@@ -199,33 +287,31 @@ class _RemoteParticipantWidgetState
|
|||||||
}
|
}
|
||||||
|
|
||||||
class InteractiveParticipantWidget extends StatelessWidget {
|
class InteractiveParticipantWidget extends StatelessWidget {
|
||||||
final double? width;
|
final double? avatarSize;
|
||||||
final double? height;
|
final bool isList;
|
||||||
final Color? color;
|
|
||||||
final bool isFixedAvatar;
|
|
||||||
final ParticipantTrack participant;
|
final ParticipantTrack participant;
|
||||||
final Function() onTap;
|
final Function? onTap;
|
||||||
|
final EdgeInsets? padding;
|
||||||
|
|
||||||
const InteractiveParticipantWidget({
|
const InteractiveParticipantWidget({
|
||||||
super.key,
|
super.key,
|
||||||
this.width,
|
this.avatarSize,
|
||||||
this.height,
|
this.isList = false,
|
||||||
this.color,
|
this.padding,
|
||||||
this.isFixedAvatar = false,
|
|
||||||
required this.participant,
|
required this.participant,
|
||||||
required this.onTap,
|
this.onTap,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return Material(
|
||||||
child: Container(
|
color: Colors.transparent,
|
||||||
width: width,
|
child: InkWell(
|
||||||
height: height,
|
onTap: onTap != null
|
||||||
color: color,
|
? () {
|
||||||
child: ParticipantWidget.widgetFor(participant, isFixed: isFixedAvatar),
|
onTap?.call();
|
||||||
),
|
}
|
||||||
onTap: () => onTap(),
|
: null,
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
if (participant.participant is LocalParticipant) return;
|
if (participant.participant is LocalParticipant) return;
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
@@ -237,6 +323,15 @@ class InteractiveParticipantWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
child: Container(
|
||||||
|
child: ParticipantWidget.widgetFor(
|
||||||
|
participant,
|
||||||
|
avatarSize: avatarSize,
|
||||||
|
isList: isList,
|
||||||
|
padding: padding,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ class ParticipantInfoWidget extends StatelessWidget {
|
|||||||
final bool audioAvailable;
|
final bool audioAvailable;
|
||||||
final ConnectionQuality connectionQuality;
|
final ConnectionQuality connectionQuality;
|
||||||
final bool isScreenShare;
|
final bool isScreenShare;
|
||||||
|
final bool isList;
|
||||||
|
|
||||||
const ParticipantInfoWidget({
|
const ParticipantInfoWidget({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -16,10 +17,69 @@ class ParticipantInfoWidget extends StatelessWidget {
|
|||||||
this.audioAvailable = true,
|
this.audioAvailable = true,
|
||||||
this.connectionQuality = ConnectionQuality.unknown,
|
this.connectionQuality = ConnectionQuality.unknown,
|
||||||
this.isScreenShare = false,
|
this.isScreenShare = false,
|
||||||
|
this.isList = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Container(
|
Widget build(BuildContext context) {
|
||||||
|
if (isList) {
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (title != null)
|
||||||
|
Text(
|
||||||
|
title!,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
).padding(left: 2),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
isScreenShare
|
||||||
|
? const Icon(
|
||||||
|
Symbols.monitor,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 16,
|
||||||
|
)
|
||||||
|
: Icon(
|
||||||
|
audioAvailable ? Symbols.mic : Symbols.mic_off,
|
||||||
|
color: audioAvailable ? Colors.white : Colors.red,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
const Gap(3),
|
||||||
|
if (connectionQuality != ConnectionQuality.unknown)
|
||||||
|
Icon(
|
||||||
|
{
|
||||||
|
ConnectionQuality.excellent: Symbols.signal_cellular_alt,
|
||||||
|
ConnectionQuality.good: Symbols.signal_cellular_alt_2_bar,
|
||||||
|
ConnectionQuality.poor: Symbols.signal_cellular_alt_1_bar,
|
||||||
|
}[connectionQuality],
|
||||||
|
color: {
|
||||||
|
ConnectionQuality.excellent: Colors.green,
|
||||||
|
ConnectionQuality.good: Colors.orange,
|
||||||
|
ConnectionQuality.poor: Colors.red,
|
||||||
|
}[connectionQuality],
|
||||||
|
size: 16,
|
||||||
|
)
|
||||||
|
else
|
||||||
|
const SizedBox(
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Colors.white,
|
||||||
|
strokeWidth: 2,
|
||||||
|
),
|
||||||
|
).padding(all: 3),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container(
|
||||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.75),
|
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.75),
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 7,
|
vertical: 7,
|
||||||
@@ -77,3 +137,4 @@ class ParticipantInfoWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@@ -16,12 +16,7 @@ class ConnectionIndicator extends StatelessWidget {
|
|||||||
final ws = context.watch<WebSocketProvider>();
|
final ws = context.watch<WebSocketProvider>();
|
||||||
final cfg = context.watch<ConfigProvider>();
|
final cfg = context.watch<ConfigProvider>();
|
||||||
|
|
||||||
final marginLeft =
|
final marginLeft = cfg.drawerIsCollapsed ? 0.0 : 80.0;
|
||||||
cfg.drawerIsCollapsed
|
|
||||||
? 0.0
|
|
||||||
: cfg.drawerIsExpanded
|
|
||||||
? 304.0
|
|
||||||
: 80.0;
|
|
||||||
|
|
||||||
return ListenableBuilder(
|
return ListenableBuilder(
|
||||||
listenable: ws,
|
listenable: ws,
|
||||||
@@ -35,10 +30,10 @@ class ConnectionIndicator extends StatelessWidget {
|
|||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child: Material(
|
child: Material(
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(16))),
|
||||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
child:
|
child: ua.isAuthorized
|
||||||
ua.isAuthorized
|
|
||||||
? Row(
|
? Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@@ -47,21 +42,30 @@ class ConnectionIndicator extends StatelessWidget {
|
|||||||
if (ws.isBusy)
|
if (ws.isBusy)
|
||||||
Text(
|
Text(
|
||||||
'serverConnecting',
|
'serverConnecting',
|
||||||
).tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer)
|
).tr().textColor(Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onSecondaryContainer)
|
||||||
else if (!ws.isConnected)
|
else if (!ws.isConnected)
|
||||||
Text(
|
Text(
|
||||||
'serverDisconnected',
|
'serverDisconnected',
|
||||||
).tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer)
|
).tr().textColor(Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onSecondaryContainer)
|
||||||
else
|
else
|
||||||
Text(
|
Text(
|
||||||
'serverConnected',
|
'serverConnected',
|
||||||
).tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer),
|
).tr().textColor(Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onSecondaryContainer),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
if (ws.isBusy)
|
if (ws.isBusy)
|
||||||
const CircularProgressIndicator(
|
const CircularProgressIndicator(
|
||||||
strokeWidth: 2.5,
|
strokeWidth: 2.5,
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
).width(12).height(12).padding(horizontal: 4, right: 4)
|
)
|
||||||
|
.width(12)
|
||||||
|
.height(12)
|
||||||
|
.padding(horizontal: 4, right: 4)
|
||||||
else if (!ws.isConnected)
|
else if (!ws.isConnected)
|
||||||
const Icon(Symbols.power_off, size: 18)
|
const Icon(Symbols.power_off, size: 18)
|
||||||
else
|
else
|
||||||
@@ -69,7 +73,9 @@ class ConnectionIndicator extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
).padding(horizontal: 8, vertical: 4)
|
).padding(horizontal: 8, vertical: 4)
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
).opacity(show ? 1 : 0, animate: true).animate(const Duration(milliseconds: 300), Curves.easeInOut),
|
)
|
||||||
|
.opacity(show ? 1 : 0, animate: true)
|
||||||
|
.animate(const Duration(milliseconds: 300), Curves.easeInOut),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (!ws.isConnected && !ws.isBusy) {
|
if (!ws.isConnected && !ws.isBusy) {
|
||||||
ws.connect();
|
ws.connect();
|
||||||
|
@@ -26,9 +26,7 @@ class ContextMenuArea extends StatelessWidget {
|
|||||||
final cfg = context.read<ConfigProvider>();
|
final cfg = context.read<ConfigProvider>();
|
||||||
if (!cfg.drawerIsCollapsed) {
|
if (!cfg.drawerIsCollapsed) {
|
||||||
// Leave padding for side navigation
|
// Leave padding for side navigation
|
||||||
mousePosition = cfg.drawerIsExpanded
|
mousePosition = mousePosition.copyWith(dx: mousePosition.dx - 80 * 2);
|
||||||
? mousePosition.copyWith(dx: mousePosition.dx - 304 * 2)
|
|
||||||
: mousePosition.copyWith(dx: mousePosition.dx - 80 * 2);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@@ -40,7 +38,8 @@ class ContextMenuArea extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showMenu(BuildContext context, Offset mousePosition) async {
|
void _showMenu(BuildContext context, Offset mousePosition) async {
|
||||||
final menu = contextMenu.copyWith(position: contextMenu.position ?? mousePosition);
|
final menu =
|
||||||
|
contextMenu.copyWith(position: contextMenu.position ?? mousePosition);
|
||||||
final value = await showContextMenu(context, contextMenu: menu);
|
final value = await showContextMenu(context, contextMenu: menu);
|
||||||
onItemSelected?.call(value);
|
onItemSelected?.call(value);
|
||||||
}
|
}
|
||||||
|
@@ -26,11 +26,13 @@ class _LinkPreviewWidgetState extends State<LinkPreviewWidget> {
|
|||||||
|
|
||||||
Future<void> _getLinkMeta() async {
|
Future<void> _getLinkMeta() async {
|
||||||
final linkRegex = RegExp(r'https?:\/\/[^\s/$.?#].[^\s]*');
|
final linkRegex = RegExp(r'https?:\/\/[^\s/$.?#].[^\s]*');
|
||||||
final links = linkRegex.allMatches(widget.text).map((e) => e.group(0)).toSet();
|
final links =
|
||||||
|
linkRegex.allMatches(widget.text).map((e) => e.group(0)).toSet();
|
||||||
|
|
||||||
final lp = context.read<SnLinkPreviewProvider>();
|
final lp = context.read<SnLinkPreviewProvider>();
|
||||||
|
|
||||||
final List<Future<SnLinkMeta?>> futures = links.where((e) => e != null).map((e) => lp.getLinkMeta(e!)).toList();
|
final List<Future<SnLinkMeta?>> futures =
|
||||||
|
links.where((e) => e != null).map((e) => lp.getLinkMeta(e!)).toList();
|
||||||
final results = await Future.wait(futures);
|
final results = await Future.wait(futures);
|
||||||
|
|
||||||
_links.addAll(results.where((e) => e != null).map((e) => e!).toList());
|
_links.addAll(results.where((e) => e != null).map((e) => e!).toList());
|
||||||
@@ -66,7 +68,9 @@ class _LinkPreviewEntry extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth: ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE) ? double.infinity : 480,
|
maxWidth: ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE)
|
||||||
|
? double.infinity
|
||||||
|
: 480,
|
||||||
),
|
),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child: Card(
|
child: Card(
|
||||||
@@ -74,20 +78,29 @@ class _LinkPreviewEntry extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (meta.image != null)
|
if (meta.image != null)
|
||||||
Container(
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(8),
|
||||||
|
topRight: Radius.circular(8),
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
margin: const EdgeInsets.only(bottom: 4),
|
margin: const EdgeInsets.only(bottom: 4),
|
||||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 16 / 9,
|
aspectRatio: 16 / 9,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
borderRadius:
|
||||||
|
const BorderRadius.all(Radius.circular(8)),
|
||||||
child: AutoResizeUniversalImage(
|
child: AutoResizeUniversalImage(
|
||||||
meta.image!.startsWith('//') ? 'https:${meta.image}' : meta.image!,
|
meta.image!.startsWith('//')
|
||||||
|
? 'https:${meta.image}'
|
||||||
|
: meta.image!,
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 48,
|
height: 48,
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -98,7 +111,8 @@ class _LinkPreviewEntry extends StatelessWidget {
|
|||||||
width: 36,
|
width: 36,
|
||||||
height: 36,
|
height: 36,
|
||||||
child: meta.icon!.endsWith('.svg')
|
child: meta.icon!.endsWith('.svg')
|
||||||
? SvgPicture.network(meta.icon!, width: 36, height: 36)
|
? SvgPicture.network(meta.icon!,
|
||||||
|
width: 36, height: 36)
|
||||||
: UniversalImage(
|
: UniversalImage(
|
||||||
meta.icon!,
|
meta.icon!,
|
||||||
noErrorWidget: true,
|
noErrorWidget: true,
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:animations/animations.dart';
|
|
||||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@@ -10,16 +9,10 @@ import 'package:gap/gap.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:responsive_framework/responsive_framework.dart';
|
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/channel.dart';
|
|
||||||
import 'package:surface/providers/config.dart';
|
|
||||||
import 'package:surface/providers/navigation.dart';
|
import 'package:surface/providers/navigation.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
|
||||||
import 'package:surface/providers/sn_realm.dart';
|
|
||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
import 'package:surface/widgets/universal_image.dart';
|
|
||||||
import 'package:surface/widgets/version_label.dart';
|
import 'package:surface/widgets/version_label.dart';
|
||||||
|
|
||||||
class AppNavigationDrawer extends StatefulWidget {
|
class AppNavigationDrawer extends StatefulWidget {
|
||||||
@@ -46,37 +39,18 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ua = context.read<UserProvider>();
|
final ua = context.read<UserProvider>();
|
||||||
final nav = context.watch<NavigationProvider>();
|
final nav = context.watch<NavigationProvider>();
|
||||||
final cfg = context.watch<ConfigProvider>();
|
|
||||||
|
|
||||||
final routeName = GoRouter.of(context)
|
|
||||||
.routerDelegate
|
|
||||||
.currentConfiguration
|
|
||||||
.last
|
|
||||||
.route
|
|
||||||
.name;
|
|
||||||
final showNavButtons = cfg.hideBottomNav ||
|
|
||||||
!(nav.showBottomNavScreen.contains(routeName)
|
|
||||||
? ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE)
|
|
||||||
: false);
|
|
||||||
|
|
||||||
final backgroundColor = cfg.drawerIsExpanded ? Colors.transparent : null;
|
|
||||||
|
|
||||||
return ListenableBuilder(
|
return ListenableBuilder(
|
||||||
listenable: nav,
|
listenable: nav,
|
||||||
builder: (context, _) {
|
builder: (context, _) {
|
||||||
return Drawer(
|
return Drawer(
|
||||||
elevation: widget.elevation,
|
elevation: widget.elevation,
|
||||||
backgroundColor: backgroundColor,
|
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(0))),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (!kIsWeb &&
|
if (!kIsWeb &&
|
||||||
(Platform.isWindows ||
|
(Platform.isWindows || Platform.isLinux || Platform.isMacOS))
|
||||||
Platform.isLinux ||
|
|
||||||
Platform.isMacOS) &&
|
|
||||||
!cfg.drawerIsExpanded)
|
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
@@ -89,45 +63,37 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
child: WindowTitleBarBox(),
|
child: WindowTitleBarBox(),
|
||||||
),
|
),
|
||||||
Gap(MediaQuery.of(context).padding.top),
|
Gap(MediaQuery.of(context).padding.top),
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Solar Network').bold(),
|
||||||
|
AppVersionLabel(),
|
||||||
|
],
|
||||||
|
).padding(
|
||||||
|
horizontal: 32,
|
||||||
|
vertical: 12,
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _DrawerContentList(),
|
child: ListView(
|
||||||
),
|
padding: EdgeInsets.zero,
|
||||||
if (showNavButtons)
|
children: [
|
||||||
Row(
|
...nav.destinations.mapIndexed((idx, ele) {
|
||||||
spacing: 8,
|
return ListTile(
|
||||||
children:
|
leading: ele.icon,
|
||||||
nav.destinations.where((ele) => ele.isPinned).mapIndexed(
|
title: Text(ele.label).tr(),
|
||||||
(idx, ele) {
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
return Expanded(
|
selected: nav.currentIndex == idx,
|
||||||
child: Tooltip(
|
onTap: () {
|
||||||
message: ele.label.tr(),
|
GoRouter.of(context).pushNamed(ele.screen);
|
||||||
child: IconButton(
|
|
||||||
icon: ele.icon,
|
|
||||||
color: nav.currentIndex == idx
|
|
||||||
? Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.onPrimaryContainer
|
|
||||||
: Theme.of(context).colorScheme.onSurface,
|
|
||||||
style: ButtonStyle(
|
|
||||||
backgroundColor: WidgetStatePropertyAll(
|
|
||||||
nav.currentIndex == idx
|
|
||||||
? Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.primaryContainer
|
|
||||||
: Colors.transparent,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
GoRouter.of(context).goNamed(ele.screen);
|
|
||||||
Scaffold.of(context).closeDrawer();
|
|
||||||
nav.setIndex(idx);
|
nav.setIndex(idx);
|
||||||
|
Scaffold.of(context).closeDrawer();
|
||||||
},
|
},
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
})
|
||||||
).toList(),
|
],
|
||||||
).padding(horizontal: 16, bottom: 8),
|
),
|
||||||
|
),
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
@@ -181,163 +147,3 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DrawerContentList extends StatelessWidget {
|
|
||||||
const _DrawerContentList();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final ct = context.read<ChatChannelProvider>();
|
|
||||||
final sn = context.read<SnNetworkProvider>();
|
|
||||||
final nav = context.watch<NavigationProvider>();
|
|
||||||
final rel = context.watch<SnRealmProvider>();
|
|
||||||
|
|
||||||
return PageTransitionSwitcher(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
transitionBuilder: (Widget child, Animation<double> primaryAnimation,
|
|
||||||
Animation<double> secondaryAnimation) {
|
|
||||||
return SharedAxisTransition(
|
|
||||||
animation: primaryAnimation,
|
|
||||||
secondaryAnimation: secondaryAnimation,
|
|
||||||
fillColor: Colors.transparent,
|
|
||||||
transitionType: SharedAxisTransitionType.horizontal,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: nav.focusedRealm == null
|
|
||||||
? ListView(
|
|
||||||
key: const Key('realm-list-view'),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text('Solar Network').bold(),
|
|
||||||
AppVersionLabel(),
|
|
||||||
],
|
|
||||||
).padding(
|
|
||||||
horizontal: 32,
|
|
||||||
vertical: 12,
|
|
||||||
),
|
|
||||||
...rel.availableRealms.map((ele) {
|
|
||||||
return ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
leading: AccountImage(
|
|
||||||
content: ele.avatar,
|
|
||||||
radius: 16,
|
|
||||||
),
|
|
||||||
title: Text(ele.name),
|
|
||||||
onTap: () {
|
|
||||||
nav.setFocusedRealm(ele);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
contentPadding: EdgeInsets.only(left: 28, right: 16),
|
|
||||||
leading: const Icon(Symbols.globe).padding(right: 4),
|
|
||||||
title: Text('screenRealmDiscovery').tr(),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).pushNamed('realmDiscovery');
|
|
||||||
Scaffold.of(context).closeDrawer();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: ListView(
|
|
||||||
key: ValueKey(nav.focusedRealm),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
children: [
|
|
||||||
if (nav.focusedRealm!.banner != null)
|
|
||||||
AspectRatio(
|
|
||||||
aspectRatio: 16 / 9,
|
|
||||||
child: AutoResizeUniversalImage(
|
|
||||||
sn.getAttachmentUrl(
|
|
||||||
nav.focusedRealm!.banner!,
|
|
||||||
),
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
tileColor: Theme.of(context).colorScheme.surfaceContainer,
|
|
||||||
contentPadding: EdgeInsets.only(
|
|
||||||
left: 24,
|
|
||||||
right: 16,
|
|
||||||
),
|
|
||||||
leading: AccountImage(
|
|
||||||
content: nav.focusedRealm!.avatar,
|
|
||||||
radius: 16,
|
|
||||||
),
|
|
||||||
trailing: IconButton(
|
|
||||||
icon: const Icon(Symbols.close),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
constraints: const BoxConstraints(),
|
|
||||||
visualDensity: VisualDensity.compact,
|
|
||||||
onPressed: () {
|
|
||||||
nav.setFocusedRealm(null);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
title: Text(nav.focusedRealm!.name),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).goNamed(
|
|
||||||
'realmDetail',
|
|
||||||
pathParameters: {
|
|
||||||
'alias': nav.focusedRealm!.alias,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
Scaffold.of(context).closeDrawer();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
contentPadding: EdgeInsets.only(
|
|
||||||
left: 28,
|
|
||||||
right: 8,
|
|
||||||
),
|
|
||||||
leading: const Icon(Symbols.globe),
|
|
||||||
title: Text('community').tr(),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).goNamed(
|
|
||||||
'realmCommunity',
|
|
||||||
pathParameters: {
|
|
||||||
'alias': nav.focusedRealm!.alias,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
Scaffold.of(context).closeDrawer();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
if (ct.availableChannels
|
|
||||||
.where((ele) => ele.realmId == nav.focusedRealm?.id)
|
|
||||||
.isNotEmpty)
|
|
||||||
const Divider(height: 1),
|
|
||||||
...(ct.availableChannels
|
|
||||||
.where((ele) => ele.realmId == nav.focusedRealm?.id)
|
|
||||||
.map((ele) {
|
|
||||||
return ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
contentPadding: EdgeInsets.only(
|
|
||||||
left: 28,
|
|
||||||
right: 8,
|
|
||||||
),
|
|
||||||
leading: const Icon(Symbols.tag),
|
|
||||||
title: Text(ele.name),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).goNamed(
|
|
||||||
'chatRoom',
|
|
||||||
pathParameters: {
|
|
||||||
'scope': ele.realm?.alias ?? 'global',
|
|
||||||
'alias': ele.alias,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
Scaffold.of(context).closeDrawer();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -1,10 +1,12 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
|
||||||
import 'package:surface/providers/navigation.dart';
|
import 'package:surface/providers/navigation.dart';
|
||||||
|
import 'package:surface/providers/userinfo.dart';
|
||||||
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
|
|
||||||
class AppRailNavigation extends StatefulWidget {
|
class AppRailNavigation extends StatefulWidget {
|
||||||
const AppRailNavigation({super.key});
|
const AppRailNavigation({super.key});
|
||||||
@@ -18,43 +20,59 @@ class _AppRailNavigationState extends State<AppRailNavigation> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
context.read<NavigationProvider>().autoDetectIndex(GoRouter.maybeOf(context));
|
context
|
||||||
|
.read<NavigationProvider>()
|
||||||
|
.autoDetectIndex(GoRouter.maybeOf(context));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final ua = context.watch<UserProvider>();
|
||||||
final nav = context.watch<NavigationProvider>();
|
final nav = context.watch<NavigationProvider>();
|
||||||
|
|
||||||
return ListenableBuilder(
|
return ListenableBuilder(
|
||||||
listenable: nav,
|
listenable: nav,
|
||||||
builder: (context, _) {
|
builder: (context, _) {
|
||||||
final destinations = nav.destinations.where((ele) => ele.isPinned).toList();
|
final destinations = nav.destinations.toList();
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 80,
|
width: 80,
|
||||||
child: NavigationRail(
|
child: NavigationRail(
|
||||||
selectedIndex:
|
labelType: NavigationRailLabelType.selected,
|
||||||
nav.currentIndex != null && nav.currentIndex! < nav.pinnedDestinationCount ? nav.currentIndex : null,
|
backgroundColor: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surfaceContainerLow
|
||||||
|
.withOpacity(0.5),
|
||||||
|
selectedIndex: nav.currentIndex != null &&
|
||||||
|
nav.currentIndex! < nav.destinations.length
|
||||||
|
? nav.currentIndex
|
||||||
|
: null,
|
||||||
destinations: [
|
destinations: [
|
||||||
...destinations.where((ele) => ele.isPinned).map((ele) {
|
...destinations.map((ele) {
|
||||||
return NavigationRailDestination(
|
return NavigationRailDestination(
|
||||||
icon: ele.icon,
|
icon: ele.icon,
|
||||||
label: Text(ele.label).tr(),
|
label: Text(ele.label).tr(),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
leading: const Gap(4),
|
||||||
trailing: Expanded(
|
trailing: Expanded(
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: StyledWidget(
|
child: Padding(
|
||||||
IconButton(
|
padding: EdgeInsets.only(bottom: 24),
|
||||||
icon: const Icon(Symbols.menu),
|
child: GestureDetector(
|
||||||
onPressed: () {
|
child: AccountImage(
|
||||||
Scaffold.of(context).openDrawer();
|
content: ua.user?.avatar,
|
||||||
|
fallbackWidget:
|
||||||
|
ua.isAuthorized ? null : const Icon(Symbols.login),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context).goNamed('account');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).padding(bottom: 16),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onDestinationSelected: (idx) {
|
onDestinationSelected: (idx) {
|
||||||
|
@@ -66,7 +66,9 @@ class AppScaffold extends StatelessWidget {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
extendBody: true,
|
extendBody: true,
|
||||||
extendBodyBehindAppBar: true,
|
extendBodyBehindAppBar: true,
|
||||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
backgroundColor: noBackground
|
||||||
|
? Colors.transparent
|
||||||
|
: Theme.of(context).scaffoldBackgroundColor,
|
||||||
body: SizedBox.expand(
|
body: SizedBox.expand(
|
||||||
child: noBackground
|
child: noBackground
|
||||||
? content
|
? content
|
||||||
@@ -111,7 +113,6 @@ class AppRootScaffold extends StatelessWidget {
|
|||||||
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||||
|
|
||||||
final isCollapseDrawer = cfg.drawerIsCollapsed;
|
final isCollapseDrawer = cfg.drawerIsCollapsed;
|
||||||
final isExpandedDrawer = cfg.drawerIsExpanded;
|
|
||||||
|
|
||||||
final routeName = GoRouter.of(context)
|
final routeName = GoRouter.of(context)
|
||||||
.routerDelegate
|
.routerDelegate
|
||||||
@@ -132,19 +133,7 @@ class AppRootScaffold extends StatelessWidget {
|
|||||||
? body
|
? body
|
||||||
: Row(
|
: Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
AppRailNavigation(),
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border(
|
|
||||||
right: BorderSide(
|
|
||||||
color: Theme.of(context).dividerColor,
|
|
||||||
width: 1 / devicePixelRatio,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: isExpandedDrawer
|
|
||||||
? AppNavigationDrawer(elevation: 0)
|
|
||||||
: AppRailNavigation(),
|
|
||||||
),
|
|
||||||
Expanded(child: body),
|
Expanded(child: body),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -232,10 +221,72 @@ class AppRootScaffold extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
drawer: !isExpandedDrawer ? AppNavigationDrawer() : null,
|
|
||||||
drawerEdgeDragWidth: isPopable ? 0 : null,
|
drawerEdgeDragWidth: isPopable ? 0 : null,
|
||||||
|
drawer: isCollapseDrawer ? const AppNavigationDrawer() : null,
|
||||||
bottomNavigationBar:
|
bottomNavigationBar:
|
||||||
isShowBottomNavigation ? AppBottomNavigationBar() : null,
|
isShowBottomNavigation ? AppBottomNavigationBar() : null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ResponsiveScaffold extends StatelessWidget {
|
||||||
|
final Widget aside;
|
||||||
|
final Widget? child;
|
||||||
|
final int asideFlex;
|
||||||
|
final int contentFlex;
|
||||||
|
const ResponsiveScaffold({
|
||||||
|
super.key,
|
||||||
|
required this.aside,
|
||||||
|
required this.child,
|
||||||
|
this.asideFlex = 1,
|
||||||
|
this.contentFlex = 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
static bool getIsExpand(BuildContext context) {
|
||||||
|
return ResponsiveBreakpoints.of(context).largerOrEqualTo(TABLET);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (getIsExpand(context)) {
|
||||||
|
return AppBackground(
|
||||||
|
isRoot: true,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
flex: asideFlex,
|
||||||
|
child: aside,
|
||||||
|
),
|
||||||
|
VerticalDivider(width: 1),
|
||||||
|
if (child != null && child != aside)
|
||||||
|
Flexible(flex: contentFlex, child: child!)
|
||||||
|
else
|
||||||
|
Flexible(
|
||||||
|
flex: contentFlex,
|
||||||
|
child: ResponsiveScaffoldLanding(child: null),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppBackground(isRoot: true, child: child ?? aside);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResponsiveScaffoldLanding extends StatelessWidget {
|
||||||
|
final Widget? child;
|
||||||
|
const ResponsiveScaffoldLanding({super.key, required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (ResponsiveScaffold.getIsExpand(context) || child == null) {
|
||||||
|
return AppScaffold(
|
||||||
|
noBackground: ResponsiveScaffold.getIsExpand(context),
|
||||||
|
appBar: AppBar(),
|
||||||
|
body: const SizedBox.shrink(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return child!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -4,7 +4,6 @@ import 'package:gap/gap.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:responsive_framework/responsive_framework.dart';
|
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/post.dart';
|
import 'package:surface/providers/post.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
@@ -30,19 +29,9 @@ class PostCommentQuickAction extends StatelessWidget {
|
|||||||
return Container(
|
return Container(
|
||||||
height: 240,
|
height: 240,
|
||||||
constraints: BoxConstraints(maxWidth: maxWidth ?? double.infinity),
|
constraints: BoxConstraints(maxWidth: maxWidth ?? double.infinity),
|
||||||
margin: ResponsiveBreakpoints.of(context).largerThan(MOBILE)
|
|
||||||
? const EdgeInsets.symmetric(vertical: 8)
|
|
||||||
: EdgeInsets.zero,
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: ResponsiveBreakpoints.of(context).largerThan(MOBILE)
|
borderRadius: BorderRadius.zero,
|
||||||
? const BorderRadius.all(Radius.circular(8))
|
border: Border.symmetric(
|
||||||
: BorderRadius.zero,
|
|
||||||
border: ResponsiveBreakpoints.of(context).largerThan(MOBILE)
|
|
||||||
? Border.all(
|
|
||||||
color: Theme.of(context).dividerColor,
|
|
||||||
width: 1 / devicePixelRatio,
|
|
||||||
)
|
|
||||||
: Border.symmetric(
|
|
||||||
horizontal: BorderSide(
|
horizontal: BorderSide(
|
||||||
color: Theme.of(context).dividerColor,
|
color: Theme.of(context).dividerColor,
|
||||||
width: 1 / devicePixelRatio,
|
width: 1 / devicePixelRatio,
|
||||||
@@ -190,9 +179,7 @@ class _PostCommentListPopupState extends State<PostCommentListPopup> {
|
|||||||
final ua = context.watch<UserProvider>();
|
final ua = context.watch<UserProvider>();
|
||||||
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||||
|
|
||||||
return SizedBox(
|
return Column(
|
||||||
height: MediaQuery.of(context).size.height * 0.85,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
@@ -240,7 +227,6 @@ class _PostCommentListPopupState extends State<PostCommentListPopup> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:animations/animations.dart';
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:file_saver/file_saver.dart';
|
import 'package:file_saver/file_saver.dart';
|
||||||
@@ -26,7 +25,6 @@ import 'package:surface/providers/sn_network.dart';
|
|||||||
import 'package:surface/providers/translation.dart';
|
import 'package:surface/providers/translation.dart';
|
||||||
import 'package:surface/providers/user_directory.dart';
|
import 'package:surface/providers/user_directory.dart';
|
||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/screens/post/post_detail.dart';
|
|
||||||
import 'package:surface/types/attachment.dart';
|
import 'package:surface/types/attachment.dart';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
import 'package:surface/types/reaction.dart';
|
import 'package:surface/types/reaction.dart';
|
||||||
@@ -53,6 +51,7 @@ class OpenablePostItem extends StatelessWidget {
|
|||||||
final bool showMenu;
|
final bool showMenu;
|
||||||
final bool showFullPost;
|
final bool showFullPost;
|
||||||
final bool showExpandableComments;
|
final bool showExpandableComments;
|
||||||
|
final bool useReplace;
|
||||||
final double? maxWidth;
|
final double? maxWidth;
|
||||||
final Function(SnPost data)? onChanged;
|
final Function(SnPost data)? onChanged;
|
||||||
final Function()? onDeleted;
|
final Function()? onDeleted;
|
||||||
@@ -66,6 +65,7 @@ class OpenablePostItem extends StatelessWidget {
|
|||||||
this.showMenu = true,
|
this.showMenu = true,
|
||||||
this.showFullPost = false,
|
this.showFullPost = false,
|
||||||
this.showExpandableComments = false,
|
this.showExpandableComments = false,
|
||||||
|
this.useReplace = false,
|
||||||
this.maxWidth,
|
this.maxWidth,
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
this.onDeleted,
|
this.onDeleted,
|
||||||
@@ -74,14 +74,10 @@ class OpenablePostItem extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final cfg = context.read<ConfigProvider>();
|
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
constraints: BoxConstraints(maxWidth: maxWidth ?? double.infinity),
|
constraints: BoxConstraints(maxWidth: maxWidth ?? double.infinity),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: OpenContainer(
|
child: GestureDetector(
|
||||||
closedBuilder: (_, __) => Container(
|
|
||||||
constraints: BoxConstraints(maxWidth: maxWidth ?? double.infinity),
|
|
||||||
child: PostItem(
|
child: PostItem(
|
||||||
data: data,
|
data: data,
|
||||||
maxWidth: maxWidth,
|
maxWidth: maxWidth,
|
||||||
@@ -92,22 +88,18 @@ class OpenablePostItem extends StatelessWidget {
|
|||||||
onDeleted: onDeleted,
|
onDeleted: onDeleted,
|
||||||
onSelectAnswer: onSelectAnswer,
|
onSelectAnswer: onSelectAnswer,
|
||||||
),
|
),
|
||||||
),
|
onTap: () {
|
||||||
openBuilder: (_, close) => PostDetailScreen(
|
if (useReplace) {
|
||||||
slug: data.id.toString(),
|
GoRouter.of(context)
|
||||||
preload: data,
|
.pushReplacementNamed('postDetail', pathParameters: {
|
||||||
onBack: close,
|
'slug': data.id.toString(),
|
||||||
),
|
});
|
||||||
openColor: Colors.transparent,
|
} else {
|
||||||
openElevation: 0,
|
GoRouter.of(context).pushNamed('postDetail', pathParameters: {
|
||||||
transitionType: ContainerTransitionType.fade,
|
'slug': data.id.toString(),
|
||||||
closedElevation: 0,
|
});
|
||||||
closedColor: Theme.of(context).colorScheme.surface.withOpacity(
|
}
|
||||||
cfg.prefs.getBool(kAppBackgroundStoreKey) == true ? 0 : 1,
|
},
|
||||||
),
|
|
||||||
closedShape: const RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -282,8 +274,10 @@ class _PostItemState extends State<PostItem> {
|
|||||||
final isParentAuthor = ua.isAuthorized &&
|
final isParentAuthor = ua.isAuthorized &&
|
||||||
widget.data.replyTo?.publisher.accountId == ua.user?.id;
|
widget.data.replyTo?.publisher.accountId == ua.user?.id;
|
||||||
|
|
||||||
final displayableAttachments = widget.data.preload?.attachments
|
final displayableAttachments = widget.data.body['attachments']
|
||||||
?.where((ele) =>
|
?.map((e) => SnAttachment.fromJson(e))
|
||||||
|
.cast<SnAttachment>()
|
||||||
|
.where((ele) =>
|
||||||
ele?.mediaType != SnMediaType.image ||
|
ele?.mediaType != SnMediaType.image ||
|
||||||
widget.data.type != 'article')
|
widget.data.type != 'article')
|
||||||
.toList();
|
.toList();
|
||||||
@@ -292,7 +286,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
|
|
||||||
var attachmentSize = math.min(
|
var attachmentSize = math.min(
|
||||||
MediaQuery.of(context).size.width, widget.maxWidth ?? double.infinity);
|
MediaQuery.of(context).size.width, widget.maxWidth ?? double.infinity);
|
||||||
if ((widget.data.preload?.attachments?.length ?? 0) > 1) {
|
if ((widget.data.body['attachments']?.length ?? 0) > 1) {
|
||||||
attachmentSize -= 80;
|
attachmentSize -= 80;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,7 +343,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
if (widget.data.preload?.thumbnail != null)
|
if (widget.data.body['thumbnail'] != null)
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(bottom: 8),
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@@ -369,14 +363,14 @@ class _PostItemState extends State<PostItem> {
|
|||||||
),
|
),
|
||||||
child: AutoResizeUniversalImage(
|
child: AutoResizeUniversalImage(
|
||||||
sn.getAttachmentUrl(
|
sn.getAttachmentUrl(
|
||||||
widget.data.preload!.thumbnail!.rid,
|
widget.data.body['thumbnail']['rid'],
|
||||||
),
|
),
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (widget.data.preload?.video != null)
|
if (widget.data.body['video'] != null)
|
||||||
_PostVideoPlayer(data: widget.data).padding(bottom: 8),
|
_PostVideoPlayer(data: widget.data).padding(bottom: 8),
|
||||||
if (widget.data.type == 'question')
|
if (widget.data.type == 'question')
|
||||||
_PostQuestionHint(data: widget.data).padding(bottom: 8),
|
_PostQuestionHint(data: widget.data).padding(bottom: 8),
|
||||||
@@ -463,8 +457,8 @@ class _PostItemState extends State<PostItem> {
|
|||||||
if (widget.data.repostTo != null)
|
if (widget.data.repostTo != null)
|
||||||
_PostQuoteContent(child: widget.data.repostTo!).padding(
|
_PostQuoteContent(child: widget.data.repostTo!).padding(
|
||||||
top: 4,
|
top: 4,
|
||||||
bottom: widget.data.preload?.attachments?.isNotEmpty ??
|
bottom:
|
||||||
false
|
widget.data.body['attachments'].isNotEmpty ?? false
|
||||||
? 12
|
? 12
|
||||||
: 0,
|
: 0,
|
||||||
),
|
),
|
||||||
@@ -487,11 +481,11 @@ class _PostItemState extends State<PostItem> {
|
|||||||
fit: widget.showFullPost ? BoxFit.cover : BoxFit.contain,
|
fit: widget.showFullPost ? BoxFit.cover : BoxFit.contain,
|
||||||
padding: EdgeInsets.only(left: 12, right: 12),
|
padding: EdgeInsets.only(left: 12, right: 12),
|
||||||
),
|
),
|
||||||
if (widget.data.preload?.poll != null)
|
if (widget.data.poll != null)
|
||||||
StyledWidget(Container(
|
StyledWidget(Container(
|
||||||
constraints:
|
constraints:
|
||||||
BoxConstraints(maxWidth: widget.maxWidth ?? double.infinity),
|
BoxConstraints(maxWidth: widget.maxWidth ?? double.infinity),
|
||||||
child: PostPoll(poll: widget.data.preload!.poll!),
|
child: PostPoll(poll: widget.data.poll!),
|
||||||
))
|
))
|
||||||
.padding(
|
.padding(
|
||||||
left: 12,
|
left: 12,
|
||||||
@@ -593,7 +587,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padding(bottom: widget.showCompactAvatar ? 4 : 0),
|
).padding(bottom: widget.showCompactAvatar ? 4 : 0),
|
||||||
if (widget.data.preload?.thumbnail != null)
|
if (widget.data.body['thumbnail'] != null)
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(bottom: 8),
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@@ -613,14 +607,14 @@ class _PostItemState extends State<PostItem> {
|
|||||||
),
|
),
|
||||||
child: AutoResizeUniversalImage(
|
child: AutoResizeUniversalImage(
|
||||||
sn.getAttachmentUrl(
|
sn.getAttachmentUrl(
|
||||||
widget.data.preload!.thumbnail!.rid,
|
widget.data.body['thumbnail']['rid'],
|
||||||
),
|
),
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (widget.data.preload?.video != null)
|
if (widget.data.body['video'] != null)
|
||||||
_PostVideoPlayer(data: widget.data)
|
_PostVideoPlayer(data: widget.data)
|
||||||
.padding(bottom: 8),
|
.padding(bottom: 8),
|
||||||
if (widget.data.type == 'question')
|
if (widget.data.type == 'question')
|
||||||
@@ -720,7 +714,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
_isTranslated ||
|
_isTranslated ||
|
||||||
_isTranslating) &&
|
_isTranslating) &&
|
||||||
(widget.data.repostTo != null ||
|
(widget.data.repostTo != null ||
|
||||||
(widget.data.preload?.attachments
|
(widget.data.body['attachments']
|
||||||
?.isNotEmpty ??
|
?.isNotEmpty ??
|
||||||
false))
|
false))
|
||||||
? 8
|
? 8
|
||||||
@@ -730,7 +724,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
_PostQuoteContent(child: widget.data.repostTo!)
|
_PostQuoteContent(child: widget.data.repostTo!)
|
||||||
.padding(
|
.padding(
|
||||||
bottom:
|
bottom:
|
||||||
(widget.data.preload?.attachments?.isNotEmpty ??
|
(widget.data.body['attachments']?.isNotEmpty ??
|
||||||
false)
|
false)
|
||||||
? 8
|
? 8
|
||||||
: 0,
|
: 0,
|
||||||
@@ -754,8 +748,8 @@ class _PostItemState extends State<PostItem> {
|
|||||||
padding:
|
padding:
|
||||||
EdgeInsets.only(left: widget.showAvatar ? 60 : 12, right: 12),
|
EdgeInsets.only(left: widget.showAvatar ? 60 : 12, right: 12),
|
||||||
),
|
),
|
||||||
if (widget.data.preload?.poll != null)
|
if (widget.data.poll != null)
|
||||||
PostPoll(poll: widget.data.preload!.poll!).padding(
|
PostPoll(poll: widget.data.poll!).padding(
|
||||||
left: widget.showAvatar ? 60 : 12,
|
left: widget.showAvatar ? 60 : 12,
|
||||||
right: 12,
|
right: 12,
|
||||||
top: 12,
|
top: 12,
|
||||||
@@ -816,7 +810,7 @@ class PostShareImageWidget extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (data.preload?.thumbnail != null)
|
if (data.body['thumbnail'] != null)
|
||||||
AspectRatio(
|
AspectRatio(
|
||||||
aspectRatio: 16 / 9,
|
aspectRatio: 16 / 9,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
@@ -825,7 +819,7 @@ class PostShareImageWidget extends StatelessWidget {
|
|||||||
topRight: Radius.circular(8),
|
topRight: Radius.circular(8),
|
||||||
),
|
),
|
||||||
child: AutoResizeUniversalImage(
|
child: AutoResizeUniversalImage(
|
||||||
sn.getAttachmentUrl(data.preload!.thumbnail!.rid),
|
sn.getAttachmentUrl(data.body['thumbnail']['rid']),
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
filterQuality: FilterQuality.high,
|
filterQuality: FilterQuality.high,
|
||||||
),
|
),
|
||||||
@@ -863,9 +857,13 @@ class PostShareImageWidget extends StatelessWidget {
|
|||||||
isRelativeDate: false,
|
isRelativeDate: false,
|
||||||
).padding(horizontal: 16, bottom: 8),
|
).padding(horizontal: 16, bottom: 8),
|
||||||
if (data.type != 'article' &&
|
if (data.type != 'article' &&
|
||||||
(data.preload?.attachments?.isNotEmpty ?? false))
|
(data.body['attachments']?.isNotEmpty ?? false))
|
||||||
StyledWidget(AttachmentList(
|
StyledWidget(AttachmentList(
|
||||||
data: data.preload!.attachments!,
|
data: data.body['attachments']
|
||||||
|
?.map((e) => SnAttachment.fromJson(e))
|
||||||
|
.cast<SnAttachment>()
|
||||||
|
.toList() ??
|
||||||
|
[],
|
||||||
columned: true,
|
columned: true,
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
filterQuality: FilterQuality.high,
|
filterQuality: FilterQuality.high,
|
||||||
@@ -1154,31 +1152,9 @@ class _PostHeadline extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (isEnlarge) {
|
if (isEnlarge) {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (data.preload?.thumbnail != null)
|
|
||||||
Container(
|
|
||||||
margin: const EdgeInsets.only(bottom: 8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
|
||||||
border: Border.all(
|
|
||||||
color: Theme.of(context).dividerColor,
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: AspectRatio(
|
|
||||||
aspectRatio: 16 / 9,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
|
||||||
child: AutoResizeUniversalImage(
|
|
||||||
sn.getAttachmentUrl(data.preload!.thumbnail!.rid),
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (data.body['title'] != null || (title?.isNotEmpty ?? false))
|
if (data.body['title'] != null || (title?.isNotEmpty ?? false))
|
||||||
Text(
|
Text(
|
||||||
title ?? data.body['title'],
|
title ?? data.body['title'],
|
||||||
@@ -1263,7 +1239,7 @@ class _PostAvatar extends StatelessWidget {
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
child: data.preload?.realm == null
|
child: data.realm == null
|
||||||
? AccountImage(
|
? AccountImage(
|
||||||
filterQuality: filterQuality,
|
filterQuality: filterQuality,
|
||||||
content: data.publisher.avatar,
|
content: data.publisher.avatar,
|
||||||
@@ -1279,7 +1255,7 @@ class _PostAvatar extends StatelessWidget {
|
|||||||
)
|
)
|
||||||
: AccountImage(
|
: AccountImage(
|
||||||
filterQuality: filterQuality,
|
filterQuality: filterQuality,
|
||||||
content: data.preload!.realm!.avatar,
|
content: data.realm!.avatar,
|
||||||
radius: isCompact ? 12 : 20,
|
radius: isCompact ? 12 : 20,
|
||||||
borderRadius: isCompact ? 4 : 8,
|
borderRadius: isCompact ? 4 : 8,
|
||||||
badgeOffset: Offset(-6, -4),
|
badgeOffset: Offset(-6, -4),
|
||||||
@@ -1576,6 +1552,7 @@ class _PostContentHeader extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (isCompact) {
|
if (isCompact) {
|
||||||
return Row(
|
return Row(
|
||||||
|
spacing: 4,
|
||||||
children: [
|
children: [
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -1583,7 +1560,6 @@ class _PostContentHeader extends StatelessWidget {
|
|||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
).bold(),
|
).bold(),
|
||||||
),
|
),
|
||||||
const Gap(4),
|
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
isRelativeDate
|
isRelativeDate
|
||||||
@@ -1595,6 +1571,10 @@ class _PostContentHeader extends StatelessWidget {
|
|||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
).fontSize(13).opacity(0.8),
|
).fontSize(13).opacity(0.8),
|
||||||
),
|
),
|
||||||
|
if (data.editedAt != null)
|
||||||
|
Flexible(
|
||||||
|
child: Text('postEditedHint').tr().fontSize(13).opacity(0.8),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -1604,20 +1584,20 @@ class _PostContentHeader extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(data.publisher.nick).bold(),
|
Text(data.publisher.nick).bold(),
|
||||||
if (data.preload?.realm != null)
|
if (data.realm != null)
|
||||||
const Icon(Symbols.arrow_right, size: 16)
|
const Icon(Symbols.arrow_right, size: 16)
|
||||||
.padding(horizontal: 2)
|
.padding(horizontal: 2)
|
||||||
.opacity(0.5),
|
.opacity(0.5),
|
||||||
if (data.preload?.realm != null) Text(data.preload!.realm!.name),
|
if (data.realm != null) Text(data.realm!.name),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
|
spacing: 4,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'@${data.publisher.name}',
|
'@${data.publisher.name}',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
).fontSize(13),
|
).fontSize(13),
|
||||||
const Gap(4),
|
|
||||||
Text(
|
Text(
|
||||||
isRelativeDate
|
isRelativeDate
|
||||||
? RelativeTime(context)
|
? RelativeTime(context)
|
||||||
@@ -1627,6 +1607,8 @@ class _PostContentHeader extends StatelessWidget {
|
|||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
).fontSize(13),
|
).fontSize(13),
|
||||||
|
if (data.editedAt != null)
|
||||||
|
Text('postEditedHint').tr().fontSize(13),
|
||||||
],
|
],
|
||||||
).opacity(0.8),
|
).opacity(0.8),
|
||||||
],
|
],
|
||||||
@@ -1656,7 +1638,11 @@ class _PostContentBody extends StatelessWidget {
|
|||||||
RegExp(r"^:([-\w]+):$").hasMatch(data.body['content'] ?? ''),
|
RegExp(r"^:([-\w]+):$").hasMatch(data.body['content'] ?? ''),
|
||||||
textScaler: isEnlarge ? TextScaler.linear(1.1) : null,
|
textScaler: isEnlarge ? TextScaler.linear(1.1) : null,
|
||||||
content: text,
|
content: text,
|
||||||
attachments: data.preload?.attachments,
|
attachments: data.body['attachments']
|
||||||
|
?.map((e) => SnAttachment.fromJson(e))
|
||||||
|
.cast<SnAttachment>()
|
||||||
|
.toList() ??
|
||||||
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isSelectable) {
|
if (isSelectable) {
|
||||||
@@ -1714,14 +1700,14 @@ class _PostQuoteContent extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
).padding(horizontal: 16),
|
).padding(horizontal: 16),
|
||||||
if (child.type != 'article' &&
|
if (child.type != 'article' &&
|
||||||
(child.preload?.attachments?.isNotEmpty ?? false))
|
(child.body['attachments']?.isNotEmpty ?? false))
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
bottomLeft: Radius.circular(8),
|
bottomLeft: Radius.circular(8),
|
||||||
bottomRight: Radius.circular(8),
|
bottomRight: Radius.circular(8),
|
||||||
),
|
),
|
||||||
child: AttachmentList(
|
child: AttachmentList(
|
||||||
data: child.preload!.attachments!,
|
data: child.body['attachments']!,
|
||||||
maxHeight: 360,
|
maxHeight: 360,
|
||||||
minWidth: 640,
|
minWidth: 640,
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
@@ -2070,8 +2056,6 @@ class _PostFeaturedCommentState extends State<_PostFeaturedComment> {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
useRootNavigator: true,
|
|
||||||
isScrollControlled: true,
|
|
||||||
builder: (context) => PostCommentListPopup(
|
builder: (context) => PostCommentListPopup(
|
||||||
post: widget.data,
|
post: widget.data,
|
||||||
commentCount: widget.data.metric.replyCount,
|
commentCount: widget.data.metric.replyCount,
|
||||||
@@ -2360,7 +2344,7 @@ class _PostVideoPlayer extends StatelessWidget {
|
|||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
child: AttachmentItem(
|
child: AttachmentItem(
|
||||||
data: data.preload!.video!,
|
data: data.body['video'],
|
||||||
heroTag: 'post-video-${data.id}',
|
heroTag: 'post-video-${data.id}',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
||||||
#include <bitsdojo_window_linux/bitsdojo_window_plugin.h>
|
#include <bitsdojo_window_linux/bitsdojo_window_plugin.h>
|
||||||
#include <fast_rsa/fast_rsa_plugin.h>
|
#include <fast_rsa/fast_rsa_plugin.h>
|
||||||
#include <file_saver/file_saver_plugin.h>
|
#include <file_saver/file_saver_plugin.h>
|
||||||
@@ -23,6 +24,9 @@
|
|||||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
|
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
|
||||||
|
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) bitsdojo_window_linux_registrar =
|
g_autoptr(FlPluginRegistrar) bitsdojo_window_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "BitsdojoWindowPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "BitsdojoWindowPlugin");
|
||||||
bitsdojo_window_plugin_register_with_registrar(bitsdojo_window_linux_registrar);
|
bitsdojo_window_plugin_register_with_registrar(bitsdojo_window_linux_registrar);
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
audioplayers_linux
|
||||||
bitsdojo_window_linux
|
bitsdojo_window_linux
|
||||||
fast_rsa
|
fast_rsa
|
||||||
file_saver
|
file_saver
|
||||||
@@ -22,7 +23,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
croppy
|
croppy
|
||||||
media_kit_native_event_loop
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import audioplayers_darwin
|
||||||
import bitsdojo_window_macos
|
import bitsdojo_window_macos
|
||||||
import connectivity_plus
|
import connectivity_plus
|
||||||
import device_info_plus
|
import device_info_plus
|
||||||
@@ -23,13 +24,13 @@ import gal
|
|||||||
import hotkey_manager_macos
|
import hotkey_manager_macos
|
||||||
import in_app_review
|
import in_app_review
|
||||||
import livekit_client
|
import livekit_client
|
||||||
|
import livekit_noise_filter
|
||||||
import local_notifier
|
import local_notifier
|
||||||
import media_kit_libs_macos_video
|
import media_kit_libs_macos_video
|
||||||
import media_kit_video
|
import media_kit_video
|
||||||
import package_info_plus
|
import package_info_plus
|
||||||
import pasteboard
|
import pasteboard
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import screen_brightness_macos
|
|
||||||
import share_plus
|
import share_plus
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import sqflite_darwin
|
import sqflite_darwin
|
||||||
@@ -37,9 +38,11 @@ import sqlite3_flutter_libs
|
|||||||
import tray_manager
|
import tray_manager
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
import video_compress
|
import video_compress
|
||||||
|
import volume_controller
|
||||||
import wakelock_plus
|
import wakelock_plus
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
||||||
BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin"))
|
BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin"))
|
||||||
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
@@ -58,13 +61,13 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
HotkeyManagerMacosPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerMacosPlugin"))
|
HotkeyManagerMacosPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerMacosPlugin"))
|
||||||
InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin"))
|
InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin"))
|
||||||
LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin"))
|
LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin"))
|
||||||
|
LiveKitKrispNoiseFilterPlugin.register(with: registry.registrar(forPlugin: "LiveKitKrispNoiseFilterPlugin"))
|
||||||
LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin"))
|
LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin"))
|
||||||
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
|
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
|
||||||
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
|
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
|
||||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||||
PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin"))
|
PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin"))
|
|
||||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
@@ -72,5 +75,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin"))
|
TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
VideoCompressPlugin.register(with: registry.registrar(forPlugin: "VideoCompressPlugin"))
|
VideoCompressPlugin.register(with: registry.registrar(forPlugin: "VideoCompressPlugin"))
|
||||||
|
VolumeControllerPlugin.register(with: registry.registrar(forPlugin: "VolumeControllerPlugin"))
|
||||||
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
|
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
PODS:
|
PODS:
|
||||||
|
- audioplayers_darwin (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- bitsdojo_window_macos (0.0.1):
|
- bitsdojo_window_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- connectivity_plus (0.0.1):
|
- connectivity_plus (0.0.1):
|
||||||
@@ -150,12 +152,15 @@ PODS:
|
|||||||
- flutter_webrtc
|
- flutter_webrtc
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- WebRTC-SDK (= 125.6422.06)
|
- WebRTC-SDK (= 125.6422.06)
|
||||||
|
- livekit_noise_filter (0.0.1):
|
||||||
|
- flutter_webrtc
|
||||||
|
- FlutterMacOS
|
||||||
|
- LiveKitKrispNoiseFilter (= 0.0.7)
|
||||||
|
- LiveKitKrispNoiseFilter (0.0.7)
|
||||||
- local_notifier (0.1.0):
|
- local_notifier (0.1.0):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- media_kit_libs_macos_video (1.0.4):
|
- media_kit_libs_macos_video (1.0.4):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- media_kit_native_event_loop (1.0.0):
|
|
||||||
- FlutterMacOS
|
|
||||||
- media_kit_video (0.0.1):
|
- media_kit_video (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- nanopb (3.30910.0):
|
- nanopb (3.30910.0):
|
||||||
@@ -173,8 +178,6 @@ PODS:
|
|||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- PromisesObjC (2.4.0)
|
- PromisesObjC (2.4.0)
|
||||||
- SAMKeychain (1.5.3)
|
- SAMKeychain (1.5.3)
|
||||||
- screen_brightness_macos (0.1.0):
|
|
||||||
- FlutterMacOS
|
|
||||||
- share_plus (0.0.1):
|
- share_plus (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
@@ -211,11 +214,14 @@ PODS:
|
|||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- video_compress (0.3.0):
|
- video_compress (0.3.0):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- volume_controller (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- wakelock_plus (0.0.1):
|
- wakelock_plus (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- WebRTC-SDK (125.6422.06)
|
- WebRTC-SDK (125.6422.06)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
|
- audioplayers_darwin (from `Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos`)
|
||||||
- bitsdojo_window_macos (from `Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos`)
|
- bitsdojo_window_macos (from `Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos`)
|
||||||
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
|
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
|
||||||
- croppy (from `Flutter/ephemeral/.symlinks/plugins/croppy/macos`)
|
- croppy (from `Flutter/ephemeral/.symlinks/plugins/croppy/macos`)
|
||||||
@@ -236,14 +242,13 @@ DEPENDENCIES:
|
|||||||
- hotkey_manager_macos (from `Flutter/ephemeral/.symlinks/plugins/hotkey_manager_macos/macos`)
|
- hotkey_manager_macos (from `Flutter/ephemeral/.symlinks/plugins/hotkey_manager_macos/macos`)
|
||||||
- in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`)
|
- in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`)
|
||||||
- livekit_client (from `Flutter/ephemeral/.symlinks/plugins/livekit_client/macos`)
|
- livekit_client (from `Flutter/ephemeral/.symlinks/plugins/livekit_client/macos`)
|
||||||
|
- livekit_noise_filter (from `Flutter/ephemeral/.symlinks/plugins/livekit_noise_filter/macos`)
|
||||||
- local_notifier (from `Flutter/ephemeral/.symlinks/plugins/local_notifier/macos`)
|
- local_notifier (from `Flutter/ephemeral/.symlinks/plugins/local_notifier/macos`)
|
||||||
- media_kit_libs_macos_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos`)
|
- media_kit_libs_macos_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos`)
|
||||||
- media_kit_native_event_loop (from `Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos`)
|
|
||||||
- media_kit_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos`)
|
- media_kit_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos`)
|
||||||
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
||||||
- pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`)
|
- pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`)
|
||||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- screen_brightness_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_brightness_macos/macos`)
|
|
||||||
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
|
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
|
||||||
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`)
|
- sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`)
|
||||||
@@ -251,6 +256,7 @@ DEPENDENCIES:
|
|||||||
- tray_manager (from `Flutter/ephemeral/.symlinks/plugins/tray_manager/macos`)
|
- tray_manager (from `Flutter/ephemeral/.symlinks/plugins/tray_manager/macos`)
|
||||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||||
- video_compress (from `Flutter/ephemeral/.symlinks/plugins/video_compress/macos`)
|
- video_compress (from `Flutter/ephemeral/.symlinks/plugins/video_compress/macos`)
|
||||||
|
- volume_controller (from `Flutter/ephemeral/.symlinks/plugins/volume_controller/macos`)
|
||||||
- wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
|
- wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
@@ -265,6 +271,7 @@ SPEC REPOS:
|
|||||||
- GoogleDataTransport
|
- GoogleDataTransport
|
||||||
- GoogleUtilities
|
- GoogleUtilities
|
||||||
- HotKey
|
- HotKey
|
||||||
|
- LiveKitKrispNoiseFilter
|
||||||
- nanopb
|
- nanopb
|
||||||
- OrderedSet
|
- OrderedSet
|
||||||
- PromisesObjC
|
- PromisesObjC
|
||||||
@@ -273,6 +280,8 @@ SPEC REPOS:
|
|||||||
- WebRTC-SDK
|
- WebRTC-SDK
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
|
audioplayers_darwin:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos
|
||||||
bitsdojo_window_macos:
|
bitsdojo_window_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos
|
||||||
connectivity_plus:
|
connectivity_plus:
|
||||||
@@ -313,12 +322,12 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/in_app_review/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/in_app_review/macos
|
||||||
livekit_client:
|
livekit_client:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/livekit_client/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/livekit_client/macos
|
||||||
|
livekit_noise_filter:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/livekit_noise_filter/macos
|
||||||
local_notifier:
|
local_notifier:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/local_notifier/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/local_notifier/macos
|
||||||
media_kit_libs_macos_video:
|
media_kit_libs_macos_video:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos
|
||||||
media_kit_native_event_loop:
|
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos
|
|
||||||
media_kit_video:
|
media_kit_video:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
@@ -327,8 +336,6 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/pasteboard/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/pasteboard/macos
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
|
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
|
||||||
screen_brightness_macos:
|
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/screen_brightness_macos/macos
|
|
||||||
share_plus:
|
share_plus:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
@@ -343,61 +350,65 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||||
video_compress:
|
video_compress:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/video_compress/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/video_compress/macos
|
||||||
|
volume_controller:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/volume_controller/macos
|
||||||
wakelock_plus:
|
wakelock_plus:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
bitsdojo_window_macos: 44e3b8fe3dd463820e0321f6256c5b1c16bb6a00
|
audioplayers_darwin: 761f2948df701d05b5db603220c384fb55720012
|
||||||
connectivity_plus: 0a976dfd033b59192912fa3c6c7b54aab5093802
|
bitsdojo_window_macos: 7959fb0ca65a3ccda30095c181ecb856fae48ea9
|
||||||
croppy: 25a638bd7d05411d8c697f481568f261037694fc
|
connectivity_plus: 4adf20a405e25b42b9c9f87feff8f4b6fde18a4e
|
||||||
device_info_plus: 1b14eed9bf95428983aed283a8d51cce3d8c4215
|
croppy: d9bfc8c02f3cd1851f669a421df298a474b78f43
|
||||||
fast_rsa: 47a50bec1042c8c01726007dc0590a078418f997
|
device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76
|
||||||
file_picker: e716a70a9fe5fd9e09ebc922d7541464289443af
|
fast_rsa: 940a67b8d8e425f37708189361efc90be7299d66
|
||||||
file_saver: 44e6fbf666677faf097302460e214e977fdd977b
|
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
|
||||||
file_selector_macos: cc3858c981fe6889f364731200d6232dac1d812d
|
file_saver: e35bd97de451dde55ff8c38862ed7ad0f3418d0f
|
||||||
|
file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31
|
||||||
Firebase: d80354ed7f6df5f9aca55e9eb47cc4b634735eaf
|
Firebase: d80354ed7f6df5f9aca55e9eb47cc4b634735eaf
|
||||||
firebase_analytics: 75b9d9ea8b21ce77898a3a46910e2051e93db8e1
|
firebase_analytics: 2c7864ab677e8a178a6dd4126de1d19e9d9a7bf3
|
||||||
firebase_core: 1b573eac37729348cdc472516991dd7e269ae37e
|
firebase_core: 3dcdf8453dfb144a023ee70f49e0463b97177f71
|
||||||
firebase_messaging: 0620038ea399ceae2218c9087fca00a28f576209
|
firebase_messaging: 96fe41b2f8b5bee4e0f21df8d716cb8c9293448c
|
||||||
FirebaseAnalytics: 4fd42def128146e24e480e89f310e3d8534ea42b
|
FirebaseAnalytics: 4fd42def128146e24e480e89f310e3d8534ea42b
|
||||||
FirebaseCore: 99fe0c4b44a39f37d99e6404e02009d2db5d718d
|
FirebaseCore: 99fe0c4b44a39f37d99e6404e02009d2db5d718d
|
||||||
FirebaseCoreInternal: df24ce5af28864660ecbd13596fc8dd3a8c34629
|
FirebaseCoreInternal: df24ce5af28864660ecbd13596fc8dd3a8c34629
|
||||||
FirebaseInstallations: 6c963bd2a86aca0481eef4f48f5a4df783ae5917
|
FirebaseInstallations: 6c963bd2a86aca0481eef4f48f5a4df783ae5917
|
||||||
FirebaseMessaging: 487b634ccdf6f7b7ff180fdcb2a9935490f764e8
|
FirebaseMessaging: 487b634ccdf6f7b7ff180fdcb2a9935490f764e8
|
||||||
flutter_inappwebview_macos: bdf207b8f4ebd58e86ae06cd96b147de99a67c9b
|
flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d
|
||||||
flutter_timezone: 62400baa441155f2a4144188648f2ff861649747
|
flutter_timezone: d59eea86178cbd7943cd2431cc2eaa9850f935d8
|
||||||
flutter_udid: 2e7b3da4b5fdfba86a396b97898f5fe8f4ec1a52
|
flutter_udid: d26e455e8c06174e6aff476e147defc6cae38495
|
||||||
flutter_webrtc: d55fd3f5c75b42940b6b4b2cf376a5797398d1f8
|
flutter_webrtc: 377dbcebdde6fed0fc40de87bcaaa2bffcec9a88
|
||||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||||
gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5
|
gal: baecd024ebfd13c441269ca7404792a7152fde89
|
||||||
GoogleAppMeasurement: fc0817122bd4d4189164f85374e06773b9561896
|
GoogleAppMeasurement: fc0817122bd4d4189164f85374e06773b9561896
|
||||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||||
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
||||||
HotKey: 400beb7caa29054ea8d864c96f5ba7e5b4852277
|
HotKey: 400beb7caa29054ea8d864c96f5ba7e5b4852277
|
||||||
hotkey_manager_macos: 1e2edb0c7ae4fe67108af44a9d3445de41404160
|
hotkey_manager_macos: a4317849af96d2430fa89944d3c58977ca089fbe
|
||||||
in_app_review: a6a031b9acd03c7d103e341aa334adf2c493fb93
|
in_app_review: 0599bccaed5e02f6bed2b0d30d16f86b63ed8638
|
||||||
livekit_client: d03409f83df069a1bb00a4c8dc78c54fb2287262
|
livekit_client: 35690bf9861be6325a6f7d11bb38d50c7c9fed80
|
||||||
local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff
|
livekit_noise_filter: c5710c0871ef3621b48c0b44d3c3ff938ba414b2
|
||||||
media_kit_libs_macos_video: b3e2bbec2eef97c285f2b1baa7963c67c753fb82
|
LiveKitKrispNoiseFilter: efe418ceca28163ace0ff222bd2cc02384645d84
|
||||||
media_kit_native_event_loop: 81fd5b45192b72f8b5b69eaf5b540f45777eb8d5
|
local_notifier: ebf072651e35ae5e47280ad52e2707375cb2ae4e
|
||||||
media_kit_video: c75b07f14d59706c775778e4dd47dd027de8d1e5
|
media_kit_libs_macos_video: 85a23e549b5f480e72cae3e5634b5514bc692f65
|
||||||
|
media_kit_video: fa6564e3799a0a28bff39442334817088b7ca758
|
||||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||||
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||||
package_info_plus: 12f1c5c2cfe8727ca46cbd0b26677728972d9a5b
|
package_info_plus: f0052d280d17aa382b932f399edf32507174e870
|
||||||
pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99
|
pasteboard: 278d8100149f940fb795d6b3a74f0720c890ecb7
|
||||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||||
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
||||||
screen_brightness_macos: 2d6d3af2165592d9a55ffcd95b7550970e41ebda
|
share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc
|
||||||
share_plus: 1fa619de8392a4398bfaf176d441853922614e89
|
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
|
||||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||||
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
|
||||||
sqlite3: fc1400008a9b3525f5914ed715a5d1af0b8f4983
|
sqlite3: fc1400008a9b3525f5914ed715a5d1af0b8f4983
|
||||||
sqlite3_flutter_libs: 487032b9008b28de37c72a3aa66849ef3745f3e6
|
sqlite3_flutter_libs: f6acaa2172e6bb3e2e70c771661905080e8ebcf2
|
||||||
tray_manager: 9064e219c56d75c476e46b9a21182087930baf90
|
tray_manager: a104b5c81b578d83f3c3d0f40a997c8b10810166
|
||||||
url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404
|
url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673
|
||||||
video_compress: c896234f100791b5fef7f049afa38f6d2ef7b42f
|
video_compress: 752b161da855df2492dd1a8fa899743cc8fe9534
|
||||||
wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269
|
volume_controller: 5c068e6d085c80dadd33fc2c918d2114b775b3dd
|
||||||
|
wakelock_plus: 21ddc249ac4b8d018838dbdabd65c5976c308497
|
||||||
WebRTC-SDK: 79942c006ea64f6fb48d7da8a4786dfc820bc1db
|
WebRTC-SDK: 79942c006ea64f6fb48d7da8a4786dfc820bc1db
|
||||||
|
|
||||||
PODFILE CHECKSUM: c2e95c8c0fe03c5c57e438583cae4cc732296009
|
PODFILE CHECKSUM: c2e95c8c0fe03c5c57e438583cae4cc732296009
|
||||||
|
172
pubspec.lock
172
pubspec.lock
@@ -45,10 +45,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: "0c64e928dcbefddecd234205422bcfc2b5e6d31be0b86fef0d0dd48d7b4c9742"
|
sha256: "7dcbd0f87fe5f61cb28da39a1a8b70dbc106e2fe0516f7836eb7bb2948481a12"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.4"
|
version: "4.0.5"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -65,6 +65,62 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.12.0"
|
version: "2.12.0"
|
||||||
|
audioplayers:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: audioplayers
|
||||||
|
sha256: a5341380a4f1d3a10a4edde5bb75de5127fe31e0faa8c4d860e64d2f91ad84c7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.4.0"
|
||||||
|
audioplayers_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: audioplayers_android
|
||||||
|
sha256: f8c90823a45b475d2c129f85bbda9c029c8d4450b172f62e066564c6e170f69a
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.2.0"
|
||||||
|
audioplayers_darwin:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: audioplayers_darwin
|
||||||
|
sha256: "405cdbd53ebdb4623f1c5af69f275dad4f930ce895512d5261c07cd95d23e778"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.2.0"
|
||||||
|
audioplayers_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: audioplayers_linux
|
||||||
|
sha256: "7e0d081a6a527c53aef9539691258a08ff69a7dc15ef6335fbea1b4b03ebbef0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.2.0"
|
||||||
|
audioplayers_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: audioplayers_platform_interface
|
||||||
|
sha256: "77e5fa20fb4a64709158391c75c1cca69a481d35dc879b519e350a05ff520373"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.1.0"
|
||||||
|
audioplayers_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: audioplayers_web
|
||||||
|
sha256: bd99d8821114747682a2be0adcdb70233d4697af989b549d3a20a0f49f6c9b13
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.1.0"
|
||||||
|
audioplayers_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: audioplayers_windows
|
||||||
|
sha256: "871d3831c25cd2408ddc552600fd4b32fba675943e319a41284704ee038ad563"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.2.0"
|
||||||
bitsdojo_window:
|
bitsdojo_window:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -365,10 +421,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dart_webrtc
|
name: dart_webrtc
|
||||||
sha256: b34e90bc82f33c1023cf98661369c37bccd648c8a4cf882a875d9f5d8bbef694
|
sha256: "8565f1f1f412b8a6fd862f3a157560811e61eeeac26741c735a5d2ff409a0202"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.2+hotfix.1"
|
version: "1.5.3"
|
||||||
dbus:
|
dbus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -746,10 +802,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_expandable_fab
|
name: flutter_expandable_fab
|
||||||
sha256: b14caf78720a48f650e6e1a38d724e33b1f5348d646fa1c266570c31a7f87ef3
|
sha256: "4d03f54e5384897e32606e9959cef5e7857e5a203e24684f95dfbb5f7fb9b88e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0"
|
version: "2.4.1"
|
||||||
flutter_highlight:
|
flutter_highlight:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1137,10 +1193,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
sha256: "13d3349ace88f12f4a0d175eb5c12dcdd39d35c4c109a8a13dfeb6d0bd9e31c3"
|
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.3"
|
version: "4.5.4"
|
||||||
image_picker:
|
image_picker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1241,10 +1297,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: js
|
name: js
|
||||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.7"
|
version: "0.7.2"
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1325,6 +1381,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.1"
|
||||||
|
livekit_noise_filter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: livekit_noise_filter
|
||||||
|
sha256: "398bfd1cc63ada9dee9fd7ea415e2fc1e51e091a6d217aad3649b882c35c7fcb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.0"
|
||||||
local_notifier:
|
local_notifier:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1393,18 +1457,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit
|
name: media_kit
|
||||||
sha256: "1f1deee148533d75129a6f38251ff8388e33ee05fc2d20a6a80e57d6051b7b62"
|
sha256: "48c10c3785df5d88f0eef970743f8c99b2e5da2b34b9d8f9876e598f62d9e776"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.11"
|
version: "1.2.0"
|
||||||
media_kit_libs_android_video:
|
media_kit_libs_android_video:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: media_kit_libs_android_video
|
name: media_kit_libs_android_video
|
||||||
sha256: "9dd8012572e4aff47516e55f2597998f0a378e3d588d0fad0ca1f11a53ae090c"
|
sha256: adff9b571b8ead0867f9f91070f8df39562078c0eb3371d88b9029a2d547d7b7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.6"
|
version: "1.3.7"
|
||||||
media_kit_libs_ios_video:
|
media_kit_libs_ios_video:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1417,10 +1481,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: media_kit_libs_linux
|
name: media_kit_libs_linux
|
||||||
sha256: e186891c31daa6bedab4d74dcdb4e8adfccc7d786bfed6ad81fe24a3b3010310
|
sha256: "2b473399a49ec94452c4d4ae51cfc0f6585074398d74216092bf3d54aac37ecf"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.3"
|
version: "1.2.1"
|
||||||
media_kit_libs_macos_video:
|
media_kit_libs_macos_video:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1433,34 +1497,26 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit_libs_video
|
name: media_kit_libs_video
|
||||||
sha256: "20bb4aefa8fece282b59580e1cd8528117297083a6640c98c2e98cfc96b93288"
|
sha256: "958cc55e7065d9d01f52a2842dab2a0812a92add18489f1006d864fb5e42a3ef"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.6"
|
||||||
media_kit_libs_windows_video:
|
media_kit_libs_windows_video:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: media_kit_libs_windows_video
|
name: media_kit_libs_windows_video
|
||||||
sha256: "32654572167825c42c55466f5d08eee23ea11061c84aa91b09d0e0f69bdd0887"
|
sha256: dff76da2778729ab650229e6b4ec6ec111eb5151431002cbd7ea304ff1f112ab
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.10"
|
version: "1.0.11"
|
||||||
media_kit_native_event_loop:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: media_kit_native_event_loop
|
|
||||||
sha256: "7d82e3b3e9ded5c35c3146c5ba1da3118d1dd8ac3435bac7f29f458181471b40"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.9"
|
|
||||||
media_kit_video:
|
media_kit_video:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: media_kit_video
|
name: media_kit_video
|
||||||
sha256: "2cc3b966679963ba25a4ce5b771e532a521ebde7c6aa20e9802bec95d9916c8f"
|
sha256: a656a9463298c1adc64c57f2d012874f7f2900f0c614d9545a3e7b8bb9e2137b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.5"
|
version: "1.3.0"
|
||||||
menu_base:
|
menu_base:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1833,58 +1889,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: safe_local_storage
|
name: safe_local_storage
|
||||||
sha256: ede4eb6cb7d88a116b3d3bf1df70790b9e2038bc37cb19112e381217c74d9440
|
sha256: e9a21b6fec7a8aa62cc2585ff4c1b127df42f3185adbd2aca66b47abe2e80236
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "2.0.1"
|
||||||
screen_brightness:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: screen_brightness
|
|
||||||
sha256: ed8da4a4511e79422fc1aa88138e920e4008cd312b72cdaa15ccb426c0faaedd
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.2+1"
|
|
||||||
screen_brightness_android:
|
screen_brightness_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: screen_brightness_android
|
name: screen_brightness_android
|
||||||
sha256: "3df10961e3a9e968a5e076fe27e7f4741fa8a1d3950bdeb48cf121ed529d0caf"
|
sha256: "6ba1b5812f66c64e9e4892be2d36ecd34210f4e0da8bdec6a2ea34f1aa42683e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.0+2"
|
version: "2.1.1"
|
||||||
screen_brightness_ios:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: screen_brightness_ios
|
|
||||||
sha256: "99adc3ca5490b8294284aad5fcc87f061ad685050e03cf45d3d018fe398fd9a2"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.1.0"
|
|
||||||
screen_brightness_macos:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: screen_brightness_macos
|
|
||||||
sha256: "64b34e7e3f4900d7687c8e8fb514246845a73ecec05ab53483ed025bd4a899fd"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.1.0+1"
|
|
||||||
screen_brightness_platform_interface:
|
screen_brightness_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: screen_brightness_platform_interface
|
name: screen_brightness_platform_interface
|
||||||
sha256: b211d07f0c96637a15fb06f6168617e18030d5d74ad03795dd8547a52717c171
|
sha256: "737bd47b57746bc4291cab1b8a5843ee881af499514881b0247ec77447ee769c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.0"
|
version: "2.1.0"
|
||||||
screen_brightness_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: screen_brightness_windows
|
|
||||||
sha256: "9261bf33d0fc2707d8cf16339ce25768100a65e70af0fcabaf032fc12408ba86"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.1.3"
|
|
||||||
screenshot:
|
screenshot:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -2294,10 +2318,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: uri_parser
|
name: uri_parser
|
||||||
sha256: "6543c9fd86d2862fac55d800a43e67c0dcd1a41677cb69c2f8edfe73bbcf1835"
|
sha256: ff4d2c720aca3f4f7d5445e23b11b2d15ef8af5ddce5164643f38ff962dcb270
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "3.0.0"
|
||||||
url_launcher:
|
url_launcher:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -2438,10 +2462,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: volume_controller
|
name: volume_controller
|
||||||
sha256: c71d4c62631305df63b72da79089e078af2659649301807fa746088f365cb48e
|
sha256: "4c2a873c242da6ce69ae1d17c256c5626e0c481be1824d6c5fc95e68c31f3b36"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.8"
|
version: "3.3.2"
|
||||||
wakelock_plus:
|
wakelock_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -2494,10 +2518,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webrtc_interface
|
name: webrtc_interface
|
||||||
sha256: e05f00091c9c70a15bab4ccb1b6c46d9a16a6075002f02cfac3641eccb05e25d
|
sha256: e92afec11152a9ccb5c9f35482754edd99696e886ab6acaf90c06dd2d09f09eb
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1+hotfix.1"
|
version: "1.2.2+hotfix.1"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 2.4.2+84
|
version: 2.4.2+86
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.4
|
sdk: ^3.5.4
|
||||||
@@ -143,6 +143,8 @@ dependencies:
|
|||||||
timelines_plus: ^1.0.6
|
timelines_plus: ^1.0.6
|
||||||
latlong2: ^0.9.1
|
latlong2: ^0.9.1
|
||||||
crypto: ^3.0.6
|
crypto: ^3.0.6
|
||||||
|
audioplayers: ^6.4.0
|
||||||
|
livekit_noise_filter: ^0.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@@ -180,6 +182,8 @@ flutter:
|
|||||||
- assets/icon/tray-icon.ico
|
- assets/icon/tray-icon.ico
|
||||||
- assets/icon/tray-icon.png
|
- assets/icon/tray-icon.png
|
||||||
- assets/icon/kanban-1st.jpg
|
- assets/icon/kanban-1st.jpg
|
||||||
|
- assets/audio/sfx/
|
||||||
|
- assets/audio/notify/
|
||||||
- assets/translations/
|
- assets/translations/
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <audioplayers_windows/audioplayers_windows_plugin.h>
|
||||||
#include <bitsdojo_window_windows/bitsdojo_window_plugin.h>
|
#include <bitsdojo_window_windows/bitsdojo_window_plugin.h>
|
||||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||||
#include <fast_rsa/fast_rsa_plugin.h>
|
#include <fast_rsa/fast_rsa_plugin.h>
|
||||||
@@ -24,13 +25,15 @@
|
|||||||
#include <media_kit_video/media_kit_video_plugin_c_api.h>
|
#include <media_kit_video/media_kit_video_plugin_c_api.h>
|
||||||
#include <pasteboard/pasteboard_plugin.h>
|
#include <pasteboard/pasteboard_plugin.h>
|
||||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||||
#include <screen_brightness_windows/screen_brightness_windows_plugin.h>
|
|
||||||
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||||
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
|
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
|
||||||
#include <tray_manager/tray_manager_plugin.h>
|
#include <tray_manager/tray_manager_plugin.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
#include <volume_controller/volume_controller_plugin_c_api.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
AudioplayersWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
|
||||||
BitsdojoWindowPluginRegisterWithRegistrar(
|
BitsdojoWindowPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("BitsdojoWindowPlugin"));
|
registry->GetRegistrarForPlugin("BitsdojoWindowPlugin"));
|
||||||
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
||||||
@@ -67,8 +70,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||||||
registry->GetRegistrarForPlugin("PasteboardPlugin"));
|
registry->GetRegistrarForPlugin("PasteboardPlugin"));
|
||||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||||
ScreenBrightnessWindowsPluginRegisterWithRegistrar(
|
|
||||||
registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin"));
|
|
||||||
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
||||||
Sqlite3FlutterLibsPluginRegisterWithRegistrar(
|
Sqlite3FlutterLibsPluginRegisterWithRegistrar(
|
||||||
@@ -77,4 +78,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||||||
registry->GetRegistrarForPlugin("TrayManagerPlugin"));
|
registry->GetRegistrarForPlugin("TrayManagerPlugin"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
|
VolumeControllerPluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("VolumeControllerPluginCApi"));
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
audioplayers_windows
|
||||||
bitsdojo_window_windows
|
bitsdojo_window_windows
|
||||||
connectivity_plus
|
connectivity_plus
|
||||||
fast_rsa
|
fast_rsa
|
||||||
@@ -21,16 +22,15 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
media_kit_video
|
media_kit_video
|
||||||
pasteboard
|
pasteboard
|
||||||
permission_handler_windows
|
permission_handler_windows
|
||||||
screen_brightness_windows
|
|
||||||
share_plus
|
share_plus
|
||||||
sqlite3_flutter_libs
|
sqlite3_flutter_libs
|
||||||
tray_manager
|
tray_manager
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
|
volume_controller
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
croppy
|
croppy
|
||||||
media_kit_native_event_loop
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
Reference in New Issue
Block a user