Compare commits

...

12 Commits

Author SHA1 Message Date
95ea3e558f 🚀 Launch 1.2.1+18 2024-08-19 09:43:25 +08:00
0006a94632 🐛 Fix local db old data cause crash 2024-08-19 09:19:29 +08:00
7ea18dbe12 💄 Update styles 2024-08-19 01:54:32 +08:00
6004b74724 🚀 Launch 1.2.1+17 2024-08-19 01:35:57 +08:00
4d82ae8058 🐛 Bug fixes
⬆️ Add firebase performance
2024-08-19 01:35:38 +08:00
7fe26d0df0 🚀 Launch 1.2.1+16 2024-08-19 00:33:20 +08:00
80bade0e03 View posts posted by friends 2024-08-19 00:33:03 +08:00
b63db7fe76 👽 Support use realm alias instead of id 2024-08-19 00:14:09 +08:00
49f73f5f04 ⬆️ Support new attachments system 2024-08-18 22:51:52 +08:00
98749f42c0 ⬆️ Upgrade deps 2024-08-17 19:18:51 +08:00
f0e6bd64f4 ♻️ Refactor video player 2024-08-17 19:02:57 +08:00
3bea3a114a Post alias 2024-08-17 18:44:20 +08:00
44 changed files with 561 additions and 497 deletions

View File

@ -51,6 +51,9 @@ PODS:
- Firebase/Messaging (10.29.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 10.29.0)
- Firebase/Performance (10.29.0):
- Firebase/CoreOnly
- FirebasePerformance (~> 10.29.0)
- firebase_analytics (11.2.1):
- Firebase/Analytics (= 10.29.0)
- firebase_core
@ -66,6 +69,12 @@ PODS:
- Firebase/Messaging (= 10.29.0)
- firebase_core
- Flutter
- firebase_performance (0.10.0-4):
- Firebase/Performance (= 10.29.0)
- firebase_core
- Flutter
- FirebaseABTesting (10.29.0):
- FirebaseCore (~> 10.0)
- FirebaseAnalytics (10.29.0):
- FirebaseAnalytics/AdIdSupport (= 10.29.0)
- FirebaseCore (~> 10.0)
@ -115,6 +124,25 @@ PODS:
- GoogleUtilities/Reachability (~> 7.8)
- GoogleUtilities/UserDefaults (~> 7.8)
- nanopb (< 2.30911.0, >= 2.30908.0)
- FirebasePerformance (10.29.0):
- FirebaseCore (~> 10.5)
- FirebaseInstallations (~> 10.0)
- FirebaseRemoteConfig (~> 10.0)
- FirebaseSessions (~> 10.5)
- GoogleDataTransport (~> 9.2)
- GoogleUtilities/Environment (~> 7.13)
- GoogleUtilities/ISASwizzler (~> 7.13)
- GoogleUtilities/MethodSwizzler (~> 7.13)
- GoogleUtilities/UserDefaults (~> 7.13)
- nanopb (< 2.30911.0, >= 2.30908.0)
- FirebaseRemoteConfig (10.29.0):
- FirebaseABTesting (~> 10.0)
- FirebaseCore (~> 10.0)
- FirebaseInstallations (~> 10.0)
- FirebaseRemoteConfigInterop (~> 10.23)
- FirebaseSharedSwift (~> 10.0)
- GoogleUtilities/Environment (~> 7.8)
- "GoogleUtilities/NSData+zlib (~> 7.8)"
- FirebaseRemoteConfigInterop (10.29.0)
- FirebaseSessions (10.29.0):
- FirebaseCore (~> 10.5)
@ -125,6 +153,7 @@ PODS:
- GoogleUtilities/UserDefaults (~> 7.13)
- nanopb (< 2.30911.0, >= 2.30908.0)
- PromisesSwift (~> 2.1)
- FirebaseSharedSwift (10.29.0)
- Flutter (1.0.0)
- flutter_keyboard_visibility (0.0.1):
- Flutter
@ -168,6 +197,8 @@ PODS:
- GoogleUtilities/Environment (7.13.3):
- GoogleUtilities/Privacy
- PromisesObjC (< 3.0, >= 1.2)
- GoogleUtilities/ISASwizzler (7.13.3):
- GoogleUtilities/Privacy
- GoogleUtilities/Logger (7.13.3):
- GoogleUtilities/Environment
- GoogleUtilities/Privacy
@ -196,12 +227,6 @@ PODS:
- livekit_client (2.2.4):
- Flutter
- WebRTC-SDK (= 125.6422.04)
- media_kit_libs_ios_video (1.0.4):
- Flutter
- media_kit_native_event_loop (1.0.0):
- Flutter
- media_kit_video (0.0.1):
- Flutter
- nanopb (2.30910.0):
- nanopb/decode (= 2.30910.0)
- nanopb/encode (= 2.30910.0)
@ -223,8 +248,6 @@ PODS:
- PromisesObjC (= 2.4.0)
- protocol_handler_ios (0.0.1):
- Flutter
- screen_brightness_ios (0.1.0):
- Flutter
- SDWebImage (5.19.6):
- SDWebImage/Core (= 5.19.6)
- SDWebImage/Core (5.19.6)
@ -240,8 +263,9 @@ PODS:
- TOCropViewController (2.7.4)
- url_launcher_ios (0.0.1):
- Flutter
- volume_controller (0.0.1):
- video_player_avfoundation (0.0.1):
- Flutter
- FlutterMacOS
- wakelock_plus (0.0.1):
- Flutter
- WebRTC-SDK (125.6422.04)
@ -254,6 +278,7 @@ DEPENDENCIES:
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- firebase_performance (from `.symlinks/plugins/firebase_performance/ios`)
- Flutter (from `Flutter`)
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
@ -262,21 +287,17 @@ DEPENDENCIES:
- image_cropper (from `.symlinks/plugins/image_cropper/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- livekit_client (from `.symlinks/plugins/livekit_client/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`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- pasteboard (from `.symlinks/plugins/pasteboard/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- pointer_interceptor_ios (from `.symlinks/plugins/pointer_interceptor_ios/ios`)
- protocol_handler_ios (from `.symlinks/plugins/protocol_handler_ios/ios`)
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- volume_controller (from `.symlinks/plugins/volume_controller/ios`)
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
SPEC REPOS:
@ -284,6 +305,7 @@ SPEC REPOS:
- DKImagePickerController
- DKPhotoGallery
- Firebase
- FirebaseABTesting
- FirebaseAnalytics
- FirebaseCore
- FirebaseCoreExtension
@ -291,8 +313,11 @@ SPEC REPOS:
- FirebaseCrashlytics
- FirebaseInstallations
- FirebaseMessaging
- FirebasePerformance
- FirebaseRemoteConfig
- FirebaseRemoteConfigInterop
- FirebaseSessions
- FirebaseSharedSwift
- GoogleAppMeasurement
- GoogleDataTransport
- GoogleUtilities
@ -319,6 +344,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/firebase_crashlytics/ios"
firebase_messaging:
:path: ".symlinks/plugins/firebase_messaging/ios"
firebase_performance:
:path: ".symlinks/plugins/firebase_performance/ios"
Flutter:
:path: Flutter
flutter_keyboard_visibility:
@ -335,12 +362,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/image_picker_ios/ios"
livekit_client:
:path: ".symlinks/plugins/livekit_client/ios"
media_kit_libs_ios_video:
: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:
:path: ".symlinks/plugins/media_kit_video/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
pasteboard:
@ -353,8 +374,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/pointer_interceptor_ios/ios"
protocol_handler_ios:
:path: ".symlinks/plugins/protocol_handler_ios/ios"
screen_brightness_ios:
:path: ".symlinks/plugins/screen_brightness_ios/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
@ -363,8 +382,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/sqflite/darwin"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
volume_controller:
:path: ".symlinks/plugins/volume_controller/ios"
video_player_avfoundation:
:path: ".symlinks/plugins/video_player_avfoundation/darwin"
wakelock_plus:
:path: ".symlinks/plugins/wakelock_plus/ios"
@ -379,6 +398,8 @@ SPEC CHECKSUMS:
firebase_core: 57aeb91680e5d5e6df6b888064be7c785f146efb
firebase_crashlytics: e3d3e0c99bad5aaab5908385133dea8ec344693f
firebase_messaging: c862b3d2b973ecc769194dc8de09bd22c77ae757
firebase_performance: 8643e815a354ee94da1192cd69335a48a7b625a4
FirebaseABTesting: d87f56707159bae64e269757a6e963d490f2eebe
FirebaseAnalytics: 23717de130b779aa506e757edb9713d24b6ffeda
FirebaseCore: 30e9c1cbe3d38f5f5e75f48bfcea87d7c358ec16
FirebaseCoreExtension: 705ca5b14bf71d2564a0ddc677df1fc86ffa600f
@ -386,8 +407,11 @@ SPEC CHECKSUMS:
FirebaseCrashlytics: 34647b41e18de773717fdd348a22206f2f9bc774
FirebaseInstallations: 913cf60d0400ebd5d6b63a28b290372ab44590dd
FirebaseMessaging: 7b5d8033e183ab59eb5b852a53201559e976d366
FirebasePerformance: d0ac4aa90f8c1aedeb8d0329a56e2d77d8d9e004
FirebaseRemoteConfig: 48ef3f243742a8d72422ccfc9f986e19d7de53fd
FirebaseRemoteConfigInterop: 6efda51fb5e2f15b16585197e26eaa09574e8a4d
FirebaseSessions: dbd14adac65ce996228652c1fc3a3f576bdf3ecc
FirebaseSharedSwift: 20530f495084b8d840f78a100d8c5ee613375f6e
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
@ -399,9 +423,6 @@ SPEC CHECKSUMS:
image_cropper: 37d40f62177c101ff4c164906d259ea2c3aa70cf
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
livekit_client: d079c5f040d4bf2b80440ff0ae997725a183e4bc
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
nanopb: 438bc412db1928dac798aa6fd75726007be04262
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
pasteboard: 982969ebaa7c78af3e6cc7761e8f5e77565d9ce0
@ -411,7 +432,6 @@ SPEC CHECKSUMS:
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
protocol_handler_ios: a5db8abc38526ee326988b808be621e5fd568990
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
SDWebImage: a79252b60f4678812d94316c91da69ec83089c9f
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
@ -419,7 +439,7 @@ SPEC CHECKSUMS:
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9
video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
WebRTC-SDK: c3d69a87e7185fad3568f6f3cff7c9ac5890acf3

View File

@ -254,6 +254,7 @@
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
B1CDA9DD5B638A2BB88053CB /* [CP] Check Pods Manifest.lock */,
7356FAC42C72724B0051A465 /* [Crashlytics] Clear dSYM */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
@ -263,7 +264,7 @@
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
287A33C298CA352A7E7F32A4 /* [CP] Embed Pods Frameworks */,
0818E8E4321C0D7433E07576 /* [CP] Copy Pods Resources */,
1A9FD6BE5DEE99CDA7399504 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */,
1A9FD6BE5DEE99CDA7399504 /* [Crashlytics] Upload dSYM */,
);
buildRules = (
);
@ -366,7 +367,7 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
1A9FD6BE5DEE99CDA7399504 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */ = {
1A9FD6BE5DEE99CDA7399504 /* [Crashlytics] Upload dSYM */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -375,14 +376,14 @@
);
inputPaths = (
);
name = "FlutterFire: \"flutterfire upload-crashlytics-symbols\"";
name = "[Crashlytics] Upload dSYM";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\n#!/bin/bash\nPATH=${PATH}:$FLUTTER_ROOT/bin:$HOME/.pub-cache/bin\nflutterfire upload-crashlytics-symbols --upload-symbols-script-path=$PODS_ROOT/FirebaseCrashlytics/upload-symbols --platform=ios --apple-project-path=${SRCROOT} --env-platform-name=${PLATFORM_NAME} --env-configuration=${CONFIGURATION} --env-project-dir=${PROJECT_DIR} --env-built-products-dir=${BUILT_PRODUCTS_DIR} --env-dwarf-dsym-folder-path=${DWARF_DSYM_FOLDER_PATH} --env-dwarf-dsym-file-name=${DWARF_DSYM_FILE_NAME} --env-infoplist-path=${INFOPLIST_PATH} --default-config=default\n";
shellScript = "\n#!/bin/bash\nsleep 1 # Without this, there seems a chance that the script runs before dSYM generation is finished \n$PODS_ROOT/FirebaseCrashlytics/upload-symbols -gsp $PROJECT_DIR/Runner/GoogleService-Info.plist -p ios $DWARF_DSYM_FOLDER_PATH/$DWARF_DSYM_FILE_NAME\n";
};
259653AE41D478F4C6BAE9B2 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
@ -439,6 +440,24 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
7356FAC42C72724B0051A465 /* [Crashlytics] Clear dSYM */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "[Crashlytics] Clear dSYM";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\n#!/bin/bash\nrm -rf $DWARF_DSYM_FOLDER_PATH/$DWARF_DSYM_FILE_NAME\n";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@ -452,7 +471,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
};
B1CDA9DD5B638A2BB88053CB /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;

View File

@ -17,6 +17,7 @@ import 'package:shared_preferences/shared_preferences.dart';
class PostEditorController extends GetxController {
late final SharedPreferences _prefs;
final aliasController = TextEditingController();
final titleController = TextEditingController();
final descriptionController = TextEditingController();
final contentController = TextEditingController();
@ -30,9 +31,9 @@ class PostEditorController extends GetxController {
Rx<Realm?> realmZone = Rx(null);
Rx<DateTime?> publishedAt = Rx(null);
Rx<DateTime?> publishedUntil = Rx(null);
RxList<int> attachments = RxList<int>.empty(growable: true);
RxList<String> attachments = RxList<String>.empty(growable: true);
RxList<String> tags = RxList<String>.empty(growable: true);
Rx<int?> thumbnail = Rx(null);
Rx<String?> thumbnail = Rx(null);
RxList<int> visibleUsers = RxList.empty(growable: true);
RxList<int> invisibleUsers = RxList.empty(growable: true);
@ -115,12 +116,12 @@ class PostEditorController extends GetxController {
return showModalBottomSheet(
context: context,
builder: (context) => AttachmentEditorPopup(
usage: 'i.attachment',
pool: 'interactive',
initialAttachments: attachments,
onAdd: (int value) {
onAdd: (String value) {
attachments.add(value);
},
onRemove: (int value) {
onRemove: (String value) {
attachments.remove(value);
},
),
@ -168,6 +169,7 @@ class PostEditorController extends GetxController {
}
void currentClear() {
aliasController.clear();
titleController.clear();
descriptionController.clear();
contentController.clear();
@ -197,16 +199,23 @@ class PostEditorController extends GetxController {
type = value.type;
editTo.value = value;
realmZone.value = value.realm;
isDraft.value = value.isDraft ?? false;
aliasController.text = value.alias ?? '';
titleController.text = value.body['title'] ?? '';
descriptionController.text = value.body['description'] ?? '';
contentController.text = value.body['content'] ?? '';
publishedAt.value = value.publishedAt;
publishedUntil.value = value.publishedUntil;
tags.value =
value.body['tags']?.map((x) => x['alias']).toList() ?? List.empty();
tags.value = List.from(
value.body['tags']?.map((x) => x['alias']).toList() ?? List.empty(),
growable: true,
);
tags.refresh();
attachments.value = value.body['attachments']?.cast<int>() ?? List.empty();
attachments.value = List.from(
value.body['attachments'] ?? List.empty(),
growable: true,
);
attachments.refresh();
thumbnail.value = value.body['thumbnail'];
@ -256,6 +265,7 @@ class PostEditorController extends GetxController {
Map<String, dynamic> get payload {
return {
'alias': aliasController.text,
'title': title,
'description': description,
'content': contentController.text,
@ -277,20 +287,33 @@ class PostEditorController extends GetxController {
set payload(Map<String, dynamic> value) {
type = value['type'];
tags.value = value['tags'].map((x) => x['alias']).toList().cast<String>();
tags.value = List.from(
value['tags'].map((x) => x['alias']).toList(),
growable: true,
);
aliasController.text = value['alias'] ?? '';
titleController.text = value['title'] ?? '';
descriptionController.text = value['description'] ?? '';
contentController.text = value['content'] ?? '';
attachments.value = value['attachments'].cast<int>() ?? List.empty();
attachments.value = List.from(
value['attachments'] ?? List.empty(),
growable: true,
);
attachments.refresh();
thumbnail.value = value['thumbnail'];
visibility.value = value['visibility'];
isDraft.value = value['is_draft'];
if (value['visible_users'] != null) {
visibleUsers.value = value['visible_users'].cast<int>();
visibleUsers.value = List.from(
value['visible_users'],
growable: true,
);
}
if (value['invisible_users'] != null) {
invisibleUsers.value = value['invisible_users'].cast<int>();
invisibleUsers.value = List.from(
value['invisible_users'],
growable: true,
);
}
if (value['published_at'] != null) {
publishedAt.value = DateTime.parse(value['published_at']).toLocal();
@ -319,6 +342,7 @@ class PostEditorController extends GetxController {
bool get isNotEmpty {
return [
aliasController.text.isNotEmpty,
titleController.text.isNotEmpty,
descriptionController.text.isNotEmpty,
contentController.text.isNotEmpty,

View File

@ -9,11 +9,12 @@ class PostListController extends GetxController {
/// The polling source modifier.
/// - `0`: default recommendations
/// - `1`: shuffle mode
/// - `1`: friend mode
/// - `2`: shuffle mode
RxInt mode = 0.obs;
/// The paging controller for infinite loading.
/// Only available when mode is `0`.
/// Only available when mode is `0` or `1`.
PagingController<int, Post> pagingController =
PagingController(firstPageKey: 0);
@ -111,10 +112,23 @@ class PostListController extends GetxController {
author: author,
);
} else {
resp = await provider.listRecommendations(
pageKey,
channel: mode.value == 0 ? null : 'shuffle',
);
switch (mode.value) {
case 2:
resp = await provider.listRecommendations(
pageKey,
channel: 'shuffle',
);
break;
case 1:
resp = await provider.listRecommendations(
pageKey,
channel: 'friends',
);
break;
default:
resp = await provider.listRecommendations(pageKey);
break;
}
}
} catch (e) {
rethrow;

View File

@ -6,7 +6,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_acrylic/flutter_acrylic.dart';
import 'package:get/get.dart';
import 'package:go_router/go_router.dart';
import 'package:media_kit/media_kit.dart';
import 'package:protocol_handler/protocol_handler.dart';
import 'package:provider/provider.dart';
import 'package:solian/bootstrapper.dart';
@ -32,7 +31,6 @@ import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
MediaKit.ensureInitialized();
await Future.wait([
_initializeFirebase(),

View File

@ -5,11 +5,11 @@ class Attachment {
DateTime createdAt;
DateTime updatedAt;
DateTime? deletedAt;
String rid;
String uuid;
int size;
String name;
String alt;
String usage;
String mimetype;
String hash;
int destination;
@ -24,11 +24,11 @@ class Attachment {
required this.createdAt,
required this.updatedAt,
required this.deletedAt,
required this.rid,
required this.uuid,
required this.size,
required this.name,
required this.alt,
required this.usage,
required this.mimetype,
required this.hash,
required this.destination,
@ -40,42 +40,45 @@ class Attachment {
});
factory Attachment.fromJson(Map<String, dynamic> json) => Attachment(
id: json['id'],
createdAt: DateTime.parse(json['created_at']),
updatedAt: DateTime.parse(json['updated_at']),
deletedAt: json['deleted_at'] != null ? DateTime.parse(json['deleted_at']) : null,
uuid: json['uuid'],
size: json['size'],
name: json['name'],
alt: json['alt'],
usage: json['usage'],
mimetype: json['mimetype'],
hash: json['hash'],
destination: json['destination'],
isAnalyzed: json['is_analyzed'],
metadata: json['metadata'],
isMature: json['is_mature'],
account: json['account'] != null ? Account.fromJson(json['account']) : null,
accountId: json['account_id'],
);
id: json['id'],
createdAt: DateTime.parse(json['created_at']),
updatedAt: DateTime.parse(json['updated_at']),
deletedAt: json['deleted_at'] != null
? DateTime.parse(json['deleted_at'])
: null,
rid: json['rid'],
uuid: json['uuid'],
size: json['size'],
name: json['name'],
alt: json['alt'],
mimetype: json['mimetype'],
hash: json['hash'],
destination: json['destination'],
isAnalyzed: json['is_analyzed'],
metadata: json['metadata'],
isMature: json['is_mature'],
account:
json['account'] != null ? Account.fromJson(json['account']) : null,
accountId: json['account_id'],
);
Map<String, dynamic> toJson() => {
'id': id,
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
'deleted_at': deletedAt?.toIso8601String(),
'uuid': uuid,
'size': size,
'name': name,
'alt': alt,
'usage': usage,
'mimetype': mimetype,
'hash': hash,
'destination': destination,
'is_analyzed': isAnalyzed,
'metadata': metadata,
'is_mature': isMature,
'account': account?.toJson(),
'account_id': accountId,
};
}
'id': id,
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
'deleted_at': deletedAt?.toIso8601String(),
'rid': rid,
'uuid': uuid,
'size': size,
'name': name,
'alt': alt,
'mimetype': mimetype,
'hash': hash,
'destination': destination,
'is_analyzed': isAnalyzed,
'metadata': metadata,
'is_mature': isMature,
'account': account?.toJson(),
'account_id': accountId,
};
}

View File

@ -63,7 +63,7 @@ class Event {
class EventMessageBody {
String text;
String algorithm;
List<int>? attachments;
List<String>? attachments;
int? quoteEvent;
int? relatedEvent;
List<int>? relatedUsers;
@ -82,7 +82,7 @@ class EventMessageBody {
text: json['text'] ?? '',
algorithm: json['algorithm'] ?? 'plain',
attachments: json['attachments'] != null
? List<int>.from(json['attachments'].map((x) => x))
? List<String>.from(json['attachments']?.whereType<String>())
: null,
quoteEvent: json['quote_event'],
relatedEvent: json['related_event'],

View File

@ -8,6 +8,8 @@ class Post {
DateTime updatedAt;
DateTime? editedAt;
DateTime? deletedAt;
String? alias;
String? areaAlias;
dynamic body;
List<Tag>? tags;
List<Category>? categories;
@ -33,6 +35,8 @@ class Post {
required this.updatedAt,
required this.editedAt,
required this.deletedAt,
required this.alias,
required this.areaAlias,
required this.type,
required this.body,
required this.tags,
@ -60,6 +64,8 @@ class Post {
deletedAt: json['deleted_at'] != null
? DateTime.parse(json['deleted_at'])
: null,
alias: json['alias'],
areaAlias: json['area_alias'],
type: json['type'],
body: json['body'],
tags: json['tags']?.map((x) => Tag.fromJson(x)).toList().cast<Tag>(),
@ -101,6 +107,8 @@ class Post {
'updated_at': updatedAt.toIso8601String(),
'edited_at': editedAt?.toIso8601String(),
'deleted_at': deletedAt?.toIso8601String(),
'alias': alias,
'area_alias': areaAlias,
'type': type,
'body': body,
'tags': tags,

View File

@ -36,7 +36,7 @@ class Sticker {
String get imageUrl => ServiceFinder.buildUrl(
'files',
'/attachments/$attachmentId',
'/attachments/${attachment.rid}',
);
factory Sticker.fromJson(Map<String, dynamic> json) => Sticker(

View File

@ -148,7 +148,7 @@ class AttachmentUploaderController extends GetxController {
Future<void> uploadAttachmentWithCallback(
Uint8List data,
String path,
String usage,
String pool,
Map<String, dynamic>? metadata,
Function(Attachment?) callback,
) async {
@ -158,7 +158,7 @@ class AttachmentUploaderController extends GetxController {
final result = await _rawUploadAttachment(
data,
path,
usage,
pool,
metadata,
onProgress: (progress) {
progressOfUpload.value = progress;
@ -171,7 +171,7 @@ class AttachmentUploaderController extends GetxController {
Future<Attachment?> uploadAttachment(
Uint8List data,
String path,
String usage,
String pool,
Map<String, dynamic>? metadata,
) async {
if (isUploading.value) throw Exception('uploading blocked');
@ -180,7 +180,7 @@ class AttachmentUploaderController extends GetxController {
final result = await _rawUploadAttachment(
data,
path,
usage,
pool,
metadata,
onProgress: (progress) {
progressOfUpload.value = progress;
@ -191,14 +191,14 @@ class AttachmentUploaderController extends GetxController {
}
Future<Attachment?> _rawUploadAttachment(
Uint8List data, String path, String usage, Map<String, dynamic>? metadata,
Uint8List data, String path, String pool, Map<String, dynamic>? metadata,
{Function(double)? onProgress, Function(dynamic err)? onError}) async {
final AttachmentProvider provider = Get.find();
try {
final result = await provider.createAttachment(
data,
path,
usage,
pool,
metadata,
onProgress: onProgress,
);

View File

@ -88,7 +88,30 @@ class ChatCallProvider extends GetxController {
void initRoom() {
initHardware();
room = Room();
room = Room(
roomOptions: const RoomOptions(
dynacast: true,
adaptiveStream: true,
defaultAudioPublishOptions: AudioPublishOptions(
name: 'call_voice',
stream: 'call_stream',
),
defaultVideoPublishOptions: VideoPublishOptions(
name: 'call_video',
stream: 'call_stream',
simulcast: true,
backupVideoCodec: BackupVideoCodec(enabled: true),
),
defaultScreenShareCaptureOptions: ScreenShareCaptureOptions(
useiOSBroadcastExtension: true,
params: VideoParametersPresets.screenShareH1080FPS30,
),
defaultCameraCaptureOptions: CameraCaptureOptions(
maxFrameRate: 30,
params: VideoParametersPresets.h1080_169,
),
),
);
listener = room.createListener();
WakelockPlus.enable();
}
@ -104,28 +127,6 @@ class ChatCallProvider extends GetxController {
await room.connect(
url,
token,
roomOptions: const RoomOptions(
dynacast: true,
adaptiveStream: true,
defaultAudioPublishOptions: AudioPublishOptions(
name: 'call_voice',
stream: 'call_stream',
),
defaultVideoPublishOptions: VideoPublishOptions(
name: 'call_video',
stream: 'call_stream',
simulcast: true,
backupVideoCodec: BackupVideoCodec(enabled: true),
),
defaultScreenShareCaptureOptions: ScreenShareCaptureOptions(
useiOSBroadcastExtension: true,
params: VideoParametersPresets.screenShareH1080FPS30,
),
defaultCameraCaptureOptions: CameraCaptureOptions(
maxFrameRate: 30,
params: VideoParametersPresets.h1080_169,
),
),
fastConnectOptions: FastConnectOptions(
microphone: TrackOption(track: audioTrack.value),
camera: TrackOption(track: videoTrack.value),
@ -152,7 +153,7 @@ class ChatCallProvider extends GetxController {
void onRoomDidUpdate() => sortParticipants();
void setupRoom() {
if(isInitialized.value) return;
if (isInitialized.value) return;
sortParticipants();
room.addListener(onRoomDidUpdate);

View File

@ -20,22 +20,22 @@ class AttachmentProvider extends GetConnect {
httpClient.baseUrl = ServiceFinder.buildUrl('files', null);
}
final Map<int, Attachment> _cachedResponses = {};
final Map<String, Attachment> _cachedResponses = {};
Future<List<Attachment?>> listMetadata(
List<int> id, {
List<String> rid, {
noCache = false,
}) async {
if (id.isEmpty) return List.empty();
if (rid.isEmpty) return List.empty();
List<Attachment?> result = List.filled(id.length, null);
List<int> pendingQuery = List.empty(growable: true);
List<Attachment?> result = List.filled(rid.length, null);
List<String> pendingQuery = List.empty(growable: true);
if (!noCache) {
for (var idx = 0; idx < id.length; idx++) {
if (_cachedResponses.containsKey(id[idx])) {
result[idx] = _cachedResponses[id[idx]];
for (var idx = 0; idx < rid.length; idx++) {
if (_cachedResponses.containsKey(rid[idx])) {
result[idx] = _cachedResponses[rid[idx]];
} else {
pendingQuery.add(id[idx]);
pendingQuery.add(rid[idx]);
}
}
}
@ -52,12 +52,12 @@ class AttachmentProvider extends GetConnect {
rawOut.data!.map((x) => Attachment.fromJson(x)).toList();
for (final item in out) {
if (item.destination != 0 && item.isAnalyzed) {
_cachedResponses[item.id] = item;
_cachedResponses[item.rid] = item;
}
}
for (var i = 0; i < out.length; i++) {
for (var j = 0; j < id.length; j++) {
if (out[i].id == id[j]) {
for (var j = 0; j < rid.length; j++) {
if (out[i].rid == rid[j]) {
result[j] = out[i];
}
}
@ -66,16 +66,16 @@ class AttachmentProvider extends GetConnect {
return result;
}
Future<Attachment?> getMetadata(int id, {noCache = false}) async {
if (!noCache && _cachedResponses.containsKey(id)) {
return _cachedResponses[id]!;
Future<Attachment?> getMetadata(String rid, {noCache = false}) async {
if (!noCache && _cachedResponses.containsKey(rid)) {
return _cachedResponses[rid]!;
}
final resp = await get('/attachments/$id/meta');
final resp = await get('/attachments/$rid/meta');
if (resp.statusCode == 200) {
final result = Attachment.fromJson(resp.body);
if (result.destination != 0 && result.isAnalyzed) {
_cachedResponses[id] = result;
_cachedResponses[rid] = result;
}
return result;
}
@ -84,7 +84,7 @@ class AttachmentProvider extends GetConnect {
}
Future<Attachment> createAttachment(
Uint8List data, String path, String usage, Map<String, dynamic>? metadata,
Uint8List data, String path, String pool, Map<String, dynamic>? metadata,
{Function(double)? onProgress}) async {
final AuthProvider auth = Get.find();
if (auth.isAuthorized.isFalse) throw Exception('unauthorized');
@ -108,7 +108,7 @@ class AttachmentProvider extends GetConnect {
final payload = dio.FormData.fromMap({
'alt': fileAlt,
'file': filePayload,
'usage': usage,
'pool': pool,
if (mimetypeOverride != null) 'mimetype': mimetypeOverride,
'metadata': jsonEncode(metadata),
});
@ -133,8 +133,7 @@ class AttachmentProvider extends GetConnect {
Future<Response> updateAttachment(
int id,
String alt,
String usage, {
String alt, {
bool isMature = false,
}) async {
final AuthProvider auth = Get.find();
@ -144,7 +143,6 @@ class AttachmentProvider extends GetConnect {
var resp = await client.put('/attachments/$id', {
'alt': alt,
'usage': usage,
'is_mature': isMature,
});
@ -169,7 +167,7 @@ class AttachmentProvider extends GetConnect {
return resp;
}
void clearCache({int? id}) {
void clearCache({String? id}) {
if (id != null) {
_cachedResponses.remove(id);
} else {

View File

@ -9,13 +9,20 @@ class PostProvider extends GetConnect {
}
Future<Response> listRecommendations(int page,
{int? realm, String? channel}) async {
{String? realm, String? channel}) async {
GetConnect client;
final AuthProvider auth = Get.find();
final queries = [
'take=${10}',
'offset=$page',
if (realm != null) 'realmId=$realm',
if (realm != null) 'realm=$realm',
];
final resp = await get(
if (auth.isAuthorized.value) {
client = auth.configureClient('co');
} else {
client = ServiceFinder.configureClient('co');
}
final resp = await client.get(
channel == null
? '/recommendations?${queries.join('&')}'
: '/recommendations/$channel?${queries.join('&')}',
@ -45,14 +52,14 @@ class PostProvider extends GetConnect {
}
Future<Response> listPost(int page,
{int? realm, String? author, tag, category}) async {
{String? realm, String? author, tag, category}) async {
final queries = [
'take=${10}',
'offset=$page',
if (tag != null) 'tag=$tag',
if (category != null) 'category=$category',
if (author != null) 'author=$author',
if (realm != null) 'realmId=$realm',
if (realm != null) 'realm=$realm',
];
final resp = await get('/posts?${queries.join('&')}');
if (resp.statusCode != 200) {

View File

@ -116,7 +116,7 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> {
attachResult = await provider.createAttachment(
await file.readAsBytes(),
file.path,
'p.$position',
'avatar',
null,
);
} catch (e) {

View File

@ -66,7 +66,7 @@ class _StickerScreenState extends State<StickerScreen> {
Widget _buildEmoteEntry(Sticker item, String prefix) {
final imageUrl = ServiceFinder.buildUrl(
'files',
'/attachments/${item.attachmentId}',
'/attachments/${item.attachment.rid}',
);
return ListTile(
title: Text(item.name),

View File

@ -5,6 +5,7 @@ import 'package:solian/providers/auth.dart';
import 'package:solian/router.dart';
import 'package:solian/screens/account/notification.dart';
import 'package:solian/theme.dart';
import 'package:solian/widgets/account/signin_required_overlay.dart';
import 'package:solian/widgets/app_bar_title.dart';
import 'package:solian/widgets/current_state_action.dart';
import 'package:solian/widgets/app_bar_leading.dart';
@ -27,20 +28,18 @@ class _HomeScreenState extends State<HomeScreen>
void initState() {
super.initState();
_postController = PostListController();
_tabController = TabController(length: 2, vsync: this);
_tabController = TabController(length: 3, vsync: this);
_tabController.addListener(() {
switch (_tabController.index) {
case 0:
case 1:
if (_postController.mode.value == _tabController.index) return;
_postController.mode.value = _tabController.index;
_postController.reloadAllOver();
}
if (_postController.mode.value == _tabController.index) return;
_postController.mode.value = _tabController.index;
_postController.reloadAllOver();
});
}
@override
Widget build(BuildContext context) {
final AuthProvider auth = Get.find();
return Material(
color: Theme.of(context).colorScheme.surface,
child: Scaffold(
@ -82,6 +81,7 @@ class _HomeScreenState extends State<HomeScreen>
controller: _tabController,
tabs: [
Tab(text: 'postListNews'.tr),
Tab(text: 'postListFriends'.tr),
Tab(text: 'postListShuffle'.tr),
],
),
@ -108,6 +108,23 @@ class _HomeScreenState extends State<HomeScreen>
),
]),
),
Obx(() {
if (auth.isAuthorized.value) {
return RefreshIndicator(
onRefresh: () => _postController.reloadAllOver(),
child: CustomScrollView(slivers: [
PostWarpedListWidget(
controller: _postController.pagingController,
onUpdate: () => _postController.reloadAllOver(),
),
]),
);
} else {
return SigninRequiredOverlay(
onSignedIn: () => _postController.reloadAllOver(),
);
}
}),
PostShuffleSwiper(controller: _postController),
],
);

View File

@ -65,7 +65,7 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
final AttachmentUploaderController uploader = Get.find();
if (uploader.queueOfUpload.any(
((x) => x.usage == 'i.attachment' && x.isUploading),
((x) => x.isUploading),
)) {
context.showErrorDialog('attachmentUploadInProgress'.tr);
return;
@ -90,8 +90,8 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
if (resp.statusCode != 200) {
context.showErrorDialog(resp.bodyString);
} else {
_editorController.localClear();
_editorController.currentClear();
_editorController.localClear();
AppRouter.instance.pop(resp.body);
}
@ -176,10 +176,19 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
children: [
ListTile(
tileColor: Theme.of(context).colorScheme.surfaceContainerLow,
title: Text(
_editorController.title ?? 'title'.tr,
maxLines: 1,
overflow: TextOverflow.ellipsis,
title: Row(
children: [
Text(
_editorController.title ?? 'title'.tr,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(width: 6),
if (_editorController.aliasController.text.isNotEmpty)
Badge(
label: Text('#${_editorController.aliasController.text}'),
),
],
),
subtitle: Text(
_editorController.description ?? 'description'.tr,
@ -255,6 +264,7 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
),
],
),
if (_isBusy) const LinearProgressIndicator().animate().scaleX(),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
@ -265,10 +275,6 @@ class _PostPublishScreenState extends State<PostPublishScreen> {
Expanded(
child: ListView(
children: [
if (_isBusy)
const LinearProgressIndicator()
.animate()
.scaleX(),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 16,

View File

@ -171,7 +171,7 @@ class _RealmPostListWidgetState extends State<RealmPostListWidget> {
Response resp;
try {
resp = await provider.listPost(pageKey, realm: widget.realm.id);
resp = await provider.listPost(pageKey, realm: widget.realm.alias);
} catch (e) {
_pagingController.error = e;
return;

View File

@ -1,3 +1,4 @@
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:solian/theme.dart';
@ -29,6 +30,15 @@ class RootShell extends StatelessWidget {
Widget build(BuildContext context) {
final routeName = state.topRoute?.name;
if (routeName != null) {
FirebaseAnalytics.instance.logEvent(
name: 'screen_view',
parameters: {
'firebase_screen': routeName,
},
);
}
return Scaffold(
key: rootScaffoldKey,
drawer: SolianTheme.isLargeScreen(context)

View File

@ -13,6 +13,7 @@ const i18nEnglish = {
'more': 'More',
'share': 'Share',
'shareNoUri': 'Share text content',
'alias': 'Alias',
'feed': 'Feed',
'unlink': 'Unlink',
'feedSearch': 'Search Feed',
@ -124,6 +125,7 @@ const i18nEnglish = {
'postThumbnailAttachment': 'Attachment serial number',
'postPinned': 'Pinned',
'postListNews': 'News',
'postListFriends': 'Friends',
'postListShuffle': 'Random',
'postEditorModeStory': 'Post a post',
'postEditorModeArticle': 'Post an article',

View File

@ -21,6 +21,7 @@ const i18nSimplifiedChinese = {
'more': '更多',
'share': '分享',
'shareNoUri': '分享文字内容',
'alias': '别名',
'feed': '资讯',
'unlink': '移除链接',
'feedSearch': '搜索资讯',
@ -124,6 +125,7 @@ const i18nSimplifiedChinese = {
'articleDetail': '文章详情',
'draftBoxOpen': '打开草稿箱',
'postListNews': '新鲜事',
'postListFriends': '好友圈',
'postListShuffle': '打乱看',
'postNew': '创建新帖子',
'postNewInRealmHint': '在领域 @realm 里发表新帖子',

View File

@ -24,7 +24,6 @@ class AccountAvatar extends StatelessWidget {
if (content is String) {
direct = content.startsWith('http');
if (!isEmpty) isEmpty = content.isEmpty;
if (!isEmpty) isEmpty = content.endsWith('/attachments/0');
}
final url = direct

View File

@ -16,10 +16,12 @@ class AttachmentAttrEditorDialog extends StatefulWidget {
});
@override
State<AttachmentAttrEditorDialog> createState() => _AttachmentAttrEditorDialogState();
State<AttachmentAttrEditorDialog> createState() =>
_AttachmentAttrEditorDialogState();
}
class _AttachmentAttrEditorDialogState extends State<AttachmentAttrEditorDialog> {
class _AttachmentAttrEditorDialogState
extends State<AttachmentAttrEditorDialog> {
final _altController = TextEditingController();
bool _isBusy = false;
@ -33,11 +35,10 @@ class _AttachmentAttrEditorDialogState extends State<AttachmentAttrEditorDialog>
final resp = await provider.updateAttachment(
widget.item.id,
_altController.value.text,
widget.item.usage,
isMature: _isMature,
);
Get.find<AttachmentProvider>().clearCache(id: widget.item.id);
Get.find<AttachmentProvider>().clearCache(id: widget.item.rid);
setState(() => _isBusy = false);
return Attachment.fromJson(resp.body);
@ -109,7 +110,7 @@ class _AttachmentAttrEditorDialogState extends State<AttachmentAttrEditorDialog>
TextButton(
style: TextButton.styleFrom(
foregroundColor:
Theme.of(context).colorScheme.onSurfaceVariant),
Theme.of(context).colorScheme.onSurfaceVariant),
onPressed: () => Navigator.pop(context),
child: Text('cancel'.tr),
),

View File

@ -22,19 +22,19 @@ import 'package:solian/widgets/attachments/attachment_attr_editor.dart';
import 'package:solian/widgets/attachments/attachment_fullscreen.dart';
class AttachmentEditorPopup extends StatefulWidget {
final String usage;
final String pool;
final bool singleMode;
final bool imageOnly;
final bool autoUpload;
final double? imageMaxWidth;
final double? imageMaxHeight;
final List<int>? initialAttachments;
final void Function(int) onAdd;
final void Function(int) onRemove;
final List<String>? initialAttachments;
final void Function(String) onAdd;
final void Function(String) onRemove;
const AttachmentEditorPopup({
super.key,
required this.usage,
required this.pool,
required this.onAdd,
required this.onRemove,
this.singleMode = false,
@ -73,7 +73,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
_enqueueTaskBatch(medias.map((x) {
final file = File(x.path);
return AttachmentUploadTask(file: file, usage: widget.usage);
return AttachmentUploadTask(file: file, usage: widget.pool);
}));
} else {
final media = await _imagePicker.pickMedia(
@ -83,7 +83,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
if (media == null) return;
_enqueueTask(
AttachmentUploadTask(file: File(media.path), usage: widget.usage),
AttachmentUploadTask(file: File(media.path), usage: widget.pool),
);
}
}
@ -97,7 +97,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
final file = File(media.path);
_enqueueTask(
AttachmentUploadTask(file: file, usage: widget.usage),
AttachmentUploadTask(file: file, usage: widget.pool),
);
}
@ -113,7 +113,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
List<File> files = result.paths.map((path) => File(path!)).toList();
_enqueueTaskBatch(files.map((x) {
return AttachmentUploadTask(file: x, usage: widget.usage);
return AttachmentUploadTask(file: x, usage: widget.pool);
}));
}
@ -131,7 +131,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
final file = File(media.path);
_enqueueTask(
AttachmentUploadTask(file: file, usage: widget.usage),
AttachmentUploadTask(file: file, usage: widget.pool),
);
}
@ -181,13 +181,11 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
WidgetsBinding.instance.addPostFrameCallback((_) => controller.dispose());
if (input == null || input.isEmpty) return;
final value = int.tryParse(input);
if (value == null) return;
final AttachmentProvider attach = Get.find();
final result = await attach.getMetadata(value);
final result = await attach.getMetadata(input);
if (result != null) {
widget.onAdd(result.id);
widget.onAdd(result.rid);
setState(() => _attachments.add(result));
if (widget.singleMode) Navigator.pop(context);
}
@ -202,11 +200,11 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
_uploadController.uploadAttachmentWithCallback(
data,
'Pasted Image',
widget.usage,
widget.pool,
null,
(item) {
if (item == null) return;
widget.onAdd(item.id);
widget.onAdd(item.rid);
if (mounted) {
setState(() => _attachments.add(item));
if (widget.singleMode) Navigator.pop(context);
@ -413,11 +411,11 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
: () {
_uploadController
.performSingleTask(index)
.then((r) {
if (r == null) return;
widget.onAdd(r.id);
.then((out) {
if (out == null) return;
widget.onAdd(out.rid);
if (mounted) {
setState(() => _attachments.add(r));
setState(() => _attachments.add(out));
if (widget.singleMode) {
Navigator.pop(context);
}
@ -515,7 +513,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
),
onTap: () {
_deleteAttachment(element).then((_) {
widget.onRemove(element.id);
widget.onRemove(element.rid);
setState(() => _attachments.removeAt(index));
});
},
@ -529,7 +527,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
),
),
onTap: () {
widget.onRemove(element.id);
widget.onRemove(element.rid);
setState(() => _attachments.removeAt(index));
},
),
@ -560,7 +558,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
void _startUploading() {
_uploadController.performUploadQueue(onData: (r) {
widget.onAdd(r.id);
widget.onAdd(r.rid);
if (mounted) {
setState(() => _attachments.add(r));
if (widget.singleMode) Navigator.pop(context);
@ -584,7 +582,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
if (_uploadController.isUploading.value) return;
_enqueueTaskBatch(detail.files.map((x) {
final file = File(x.path);
return AttachmentUploadTask(file: file, usage: widget.usage);
return AttachmentUploadTask(file: file, usage: widget.pool);
}));
},
child: Column(

View File

@ -67,7 +67,7 @@ class _AttachmentFullScreenState extends State<AttachmentFullScreen> {
Future<void> _saveToAlbum() async {
final url = ServiceFinder.buildUrl(
'files',
'/attachments/${widget.item.id}',
'/attachments/${widget.item.rid}',
);
if (PlatformInfo.isWeb || PlatformInfo.isDesktop) {
@ -258,7 +258,7 @@ class _AttachmentFullScreenState extends State<AttachmentFullScreen> {
spacing: 6,
children: [
Text(
'#${widget.item.id}',
'#${widget.item.rid}',
style: metaTextStyle,
),
if (widget.item.metadata?['width'] != null &&

View File

@ -1,14 +1,14 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:chewie/chewie.dart';
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:get/get.dart';
import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.dart';
import 'package:solian/models/attachment.dart';
import 'package:solian/platform.dart';
import 'package:solian/services.dart';
import 'package:solian/widgets/sized_container.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:video_player/video_player.dart';
class AttachmentItem extends StatefulWidget {
final String parentId;
@ -91,7 +91,7 @@ class _AttachmentItemState extends State<AttachmentItem> {
launchUrlString(
ServiceFinder.buildUrl(
'files',
'/attachments/${widget.item.id}',
'/attachments/${widget.item.rid}',
),
);
},
@ -135,7 +135,7 @@ class _AttachmentItemImage extends StatelessWidget {
fit: fit,
imageUrl: ServiceFinder.buildUrl(
'files',
'/attachments/${item.id}',
'/attachments/${item.rid}',
),
progressIndicatorBuilder: (context, url, downloadProgress) {
return Center(
@ -231,17 +231,22 @@ class _AttachmentItemVideo extends StatefulWidget {
}
class _AttachmentItemVideoState extends State<_AttachmentItemVideo> {
late final _player = Player(
configuration: const PlayerConfiguration(logLevel: MPVLogLevel.error),
);
late final _controller = VideoController(_player);
VideoPlayerController? _playerController;
ChewieController? _chewieController;
bool _showContent = false;
void _startLoad() {
_player.open(
Media(ServiceFinder.buildUrl('files', '/attachments/${widget.item.id}')),
play: false,
Future<void> _startLoad() async {
final ratio = widget.item.metadata?['ratio'] ?? 16 / 9;
_playerController = VideoPlayerController.networkUrl(
Uri.parse(
ServiceFinder.buildUrl('files', '/attachments/${widget.item.rid}'),
),
);
_playerController!.initialize();
_chewieController = ChewieController(
aspectRatio: ratio,
videoPlayerController: _playerController!,
);
setState(() => _showContent = true);
}
@ -249,13 +254,22 @@ class _AttachmentItemVideoState extends State<_AttachmentItemVideo> {
@override
void initState() {
super.initState();
_showContent = widget.autoload;
if (widget.autoload) {
_startLoad();
}
}
@override
void dispose() {
_playerController?.dispose();
_chewieController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final ratio = widget.item.metadata?['ratio'] ?? 16 / 9;
if (!_showContent) {
if (!_showContent || _chewieController == null) {
return GestureDetector(
child: AspectRatio(
aspectRatio: ratio,
@ -293,15 +307,8 @@ class _AttachmentItemVideoState extends State<_AttachmentItemVideo> {
);
}
return Video(
aspectRatio: ratio,
controller: _controller,
return Chewie(
controller: _chewieController!,
);
}
@override
void dispose() {
_player.dispose();
super.dispose();
}
}

View File

@ -14,7 +14,7 @@ import 'package:solian/widgets/sized_container.dart';
class AttachmentList extends StatefulWidget {
final String parentId;
final List<int> attachmentsId;
final List<String> attachmentsId;
final bool isGrid;
final bool isForceGrid;
final bool autoload;
@ -57,10 +57,12 @@ class _AttachmentListState extends State<AttachmentList> {
}
attach.listMetadata(widget.attachmentsId).then((result) {
setState(() {
_attachmentsMeta = result;
_isLoading = false;
});
if (mounted) {
setState(() {
_attachmentsMeta = result;
_isLoading = false;
});
}
_calculateAspectRatio();
});
}
@ -70,6 +72,7 @@ class _AttachmentListState extends State<AttachmentList> {
double? consistentValue;
int portrait = 0, square = 0, landscape = 0;
for (var entry in _attachmentsMeta) {
if (entry == null) continue;
if (entry!.metadata?['ratio'] != null) {
if (entry.metadata?['ratio'] is int) {
consistentValue ??= entry.metadata?['ratio'].toDouble();
@ -111,6 +114,7 @@ class _AttachmentListState extends State<AttachmentList> {
showBadge: _attachmentsMeta.length > 1 && !widget.isGrid,
showBorder: widget.attachmentsId.length > 1,
showMature: _showMature,
autoload: widget.autoload,
onReveal: (value) {
setState(() => _showMature = value);
},
@ -138,8 +142,9 @@ class _AttachmentListState extends State<AttachmentList> {
);
}
final isNotPureImage = _attachmentsMeta
.any((x) => x?.mimetype.split('/').firstOrNull != 'image');
final isNotPureImage = _attachmentsMeta.any(
(x) => x?.mimetype.split('/').firstOrNull != 'image',
);
if (widget.isGrid && (widget.isForceGrid || !isNotPureImage)) {
const radius = BorderRadius.all(Radius.circular(8));
return GridView.builder(
@ -157,8 +162,10 @@ class _AttachmentListState extends State<AttachmentList> {
final element = _attachmentsMeta[idx];
return Container(
decoration: BoxDecoration(
border:
Border.all(color: Theme.of(context).dividerColor, width: 1),
border: Border.all(
color: Theme.of(context).dividerColor,
width: 1,
),
borderRadius: radius,
),
child: ClipRRect(
@ -328,13 +335,13 @@ class AttachmentListEntry extends StatelessWidget {
}
class AttachmentSelfContainedEntry extends StatefulWidget {
final int id;
final String rid;
final String parentId;
final bool isDense;
const AttachmentSelfContainedEntry({
super.key,
required this.id,
required this.rid,
required this.parentId,
this.isDense = false,
});
@ -353,10 +360,12 @@ class _AttachmentSelfContainedEntryState
final AttachmentProvider attachments = Get.find();
return FutureBuilder(
future: attachments.getMetadata(widget.id),
future: attachments.getMetadata(widget.rid),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Text('Loading...');
return const Center(
child: CircularProgressIndicator(),
);
}
return AttachmentListEntry(
@ -364,7 +373,6 @@ class _AttachmentSelfContainedEntryState
isDense: widget.isDense,
parentId: widget.parentId,
showMature: _showMature,
showBorder: true,
onReveal: (value) {
setState(() => _showMature = value);
},

View File

@ -39,8 +39,8 @@ class ChatEvent extends StatelessWidget {
Widget _buildAttachment(BuildContext context, {bool isMinimal = false}) {
final attachments = item.body['attachments'] != null
? List<int>.from(item.body['attachments'].map((x) => x))
: List<int>.empty();
? List<String>.from(item.body['attachments']?.whereType<String>())
: List<String>.empty();
if (attachments.isEmpty) return const SizedBox();

View File

@ -59,7 +59,7 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
final TextEditingController _textController = TextEditingController();
final FocusNode _focusNode = FocusNode();
final List<int> _attachments = List.empty(growable: true);
final List<String> _attachments = List.empty(growable: true);
Event? _editTo;
Event? _replyTo;
@ -68,7 +68,7 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
showModalBottomSheet(
context: context,
builder: (context) => AttachmentEditorPopup(
usage: 'm.attachment',
pool: 'messaging',
initialAttachments: _attachments,
onAdd: (value) {
setState(() {
@ -103,7 +103,7 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
final AttachmentUploaderController uploader = Get.find();
if (uploader.queueOfUpload.any(
((x) => x.usage == 'm.attachment' && x.isUploading),
((x) => x.isUploading),
)) {
context.showErrorDialog('attachmentUploadInProgress'.tr);
return;

View File

@ -131,7 +131,7 @@ class MarkdownTextContent extends StatelessWidget {
child: AttachmentSelfContainedEntry(
isDense: true,
parentId: parentId,
id: int.parse(segments[1]),
rid: segments[1],
),
),
).paddingSymmetric(vertical: 4);

View File

@ -19,7 +19,7 @@ class PostEditorCategoriesDialog extends StatelessWidget {
initialTags: controller.tags,
hintText: 'postTagsPlaceholder'.tr,
onUpdate: (value) {
controller.tags.value = value;
controller.tags.value = List.from(value, growable: true);
controller.tags.refresh();
},
),

View File

@ -14,12 +14,25 @@ class PostEditorOverviewDialog extends StatelessWidget {
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
autofocus: true,
autocorrect: true,
controller: controller.aliasController,
decoration: InputDecoration(
isDense: true,
border: const OutlineInputBorder(),
hintText: 'alias'.tr,
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const SizedBox(height: 16),
TextField(
autofocus: true,
autocorrect: true,
controller: controller.titleController,
decoration: InputDecoration(
border: const UnderlineInputBorder(),
isDense: true,
border: const OutlineInputBorder(),
hintText: 'title'.tr,
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
@ -33,7 +46,8 @@ class PostEditorOverviewDialog extends StatelessWidget {
keyboardType: TextInputType.multiline,
controller: controller.descriptionController,
decoration: InputDecoration(
border: const UnderlineInputBorder(),
isDense: true,
border: const OutlineInputBorder(),
hintText: 'description'.tr,
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),

View File

@ -20,7 +20,7 @@ class _PostEditorThumbnailDialogState extends State<PostEditorThumbnailDialog> {
showModalBottomSheet(
context: context,
builder: (context) => AttachmentEditorPopup(
usage: 'i.attachment',
pool: 'interactive',
singleMode: true,
imageOnly: true,
autoUpload: true,
@ -84,8 +84,7 @@ class _PostEditorThumbnailDialogState extends State<PostEditorThumbnailDialog> {
actions: [
TextButton(
onPressed: () {
widget.controller.thumbnail.value =
int.tryParse(_attachmentController.text);
widget.controller.thumbnail.value = _attachmentController.text;
Navigator.pop(context);
},
child: Text('confirm'.tr),

View File

@ -42,24 +42,30 @@ class _PostActionState extends State<PostAction> {
Future<void> _doShare({bool noUri = false}) async {
ShareResult result;
String id;
final box = context.findRenderObject() as RenderBox?;
if (widget.item.alias?.isNotEmpty ?? false) {
id = '${widget.item.areaAlias}/${widget.item.alias}';
} else {
id = '${widget.item.id}';
}
if ((PlatformInfo.isAndroid || PlatformInfo.isIOS) && !noUri) {
result = await Share.shareUri(
Uri.parse('https://solsynth.dev/posts/${widget.item.id}'),
Uri.parse('https://solsynth.dev/posts/$id'),
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
);
} else {
final extraContent = [
final extraContent = <String?>[
widget.item.body['title'],
widget.item.body['description'],
];
].where((x) => x != null && x.isNotEmpty).toList();
final isExtraNotEmpty = extraContent.any((x) => x != null);
result = await Share.share(
'postShareContent'.trParams({
'username': widget.item.author.nick,
'content':
'${extraContent.join('\n')}${isExtraNotEmpty ? '\n\n' : ''}${widget.item.body['content'] ?? 'no content'}',
'link': 'https://solsynth.dev/posts/${widget.item.id}',
'link': 'https://solsynth.dev/posts/$id',
}),
subject: 'postShareSubject'.trParams({
'username': widget.item.author.nick,
@ -96,9 +102,27 @@ class _PostActionState extends State<PostAction> {
'postActionList'.tr,
style: Theme.of(context).textTheme.headlineSmall,
),
Text(
'#${widget.item.id.toString().padLeft(8, '0')}',
style: Theme.of(context).textTheme.bodySmall,
Row(
children: [
Text(
'#${widget.item.id.toString().padLeft(8, '0')}',
style: Theme.of(context).textTheme.bodySmall,
),
if (widget.item.alias?.isNotEmpty ?? false)
Text(
'·',
style: Theme.of(context).textTheme.bodySmall,
).paddingSymmetric(horizontal: 6),
if (widget.item.alias?.isNotEmpty ?? false)
Expanded(
child: Text(
'${widget.item.areaAlias}:${widget.item.alias}',
style: Theme.of(context).textTheme.bodySmall,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
],
).paddingOnly(left: 24, right: 24, top: 32, bottom: 16),

View File

@ -78,23 +78,17 @@ class _PostItemState extends State<PostItem> {
Widget _buildThumbnail() {
if (widget.item.body['thumbnail'] == null) return const SizedBox();
const radius = BorderRadius.all(Radius.circular(8));
return AspectRatio(
aspectRatio: 16 / 9,
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context).dividerColor,
width: 0.3,
),
borderRadius: radius,
),
child: ClipRRect(
borderRadius: radius,
child: AttachmentSelfContainedEntry(
id: widget.item.body['thumbnail'],
parentId: 'p${item.id}-thumbnail',
),
final border = BorderSide(
color: Theme.of(context).dividerColor,
width: 0.3,
);
return Container(
decoration: BoxDecoration(border: Border(top: border, bottom: border)),
child: AspectRatio(
aspectRatio: 16 / 9,
child: AttachmentSelfContainedEntry(
rid: widget.item.body['thumbnail'],
parentId: 'p${item.id}-thumbnail',
),
),
);
@ -298,8 +292,8 @@ class _PostItemState extends State<PostItem> {
@override
Widget build(BuildContext context) {
final List<int> attachments = item.body['attachments'] is List
? item.body['attachments']?.cast<int>()
final List<String> attachments = item.body['attachments'] is List
? List.from(item.body['attachments']?.whereType<String>())
: List.empty();
final hasAttachment = attachments.isNotEmpty;
@ -307,7 +301,7 @@ class _PostItemState extends State<PostItem> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildThumbnail().paddingSymmetric(horizontal: 12, vertical: 4),
_buildThumbnail().paddingOnly(bottom: 8),
_buildHeader().paddingSymmetric(horizontal: 12),
_buildHeaderDivider().paddingSymmetric(horizontal: 12),
Stack(
@ -381,7 +375,7 @@ class _PostItemState extends State<PostItem> {
closedBuilder: (_, openContainer) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildThumbnail().paddingSymmetric(horizontal: 12, vertical: 4),
_buildThumbnail().paddingOnly(bottom: 4),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [

View File

@ -29,7 +29,7 @@ class _StickerUploadDialogState extends State<StickerUploadDialog> {
showModalBottomSheet(
context: context,
builder: (context) => AttachmentEditorPopup(
usage: 'sticker',
pool: 'sticker',
singleMode: true,
imageOnly: true,
autoUpload: true,

View File

@ -11,8 +11,6 @@
#include <flutter_acrylic/flutter_acrylic_plugin.h>
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
#include <flutter_webrtc/flutter_web_r_t_c_plugin.h>
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
#include <media_kit_video/media_kit_video_plugin.h>
#include <pasteboard/pasteboard_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
@ -32,12 +30,6 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_webrtc_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterWebRTCPlugin");
flutter_web_r_t_c_plugin_register_with_registrar(flutter_webrtc_registrar);
g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin");
media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar);
g_autoptr(FlPluginRegistrar) media_kit_video_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin");
media_kit_video_plugin_register_with_registrar(media_kit_video_registrar);
g_autoptr(FlPluginRegistrar) pasteboard_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin");
pasteboard_plugin_register_with_registrar(pasteboard_registrar);

View File

@ -8,14 +8,11 @@ list(APPEND FLUTTER_PLUGIN_LIST
flutter_acrylic
flutter_secure_storage_linux
flutter_webrtc
media_kit_libs_linux
media_kit_video
pasteboard
url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
media_kit_native_event_loop
)
set(PLUGIN_BUNDLED_LIBRARIES)

View File

@ -18,17 +18,15 @@ import flutter_webrtc
import gal
import livekit_client
import macos_window_utils
import media_kit_libs_macos_video
import media_kit_video
import package_info_plus
import pasteboard
import path_provider_foundation
import protocol_handler_macos
import screen_brightness_macos
import share_plus
import shared_preferences_foundation
import sqflite
import url_launcher_macos
import video_player_avfoundation
import wakelock_plus
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
@ -45,16 +43,14 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin"))
LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin"))
MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin"))
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
ProtocolHandlerMacosPlugin.register(with: registry.registrar(forPlugin: "ProtocolHandlerMacosPlugin"))
ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
}

View File

@ -163,12 +163,6 @@ PODS:
- WebRTC-SDK (= 125.6422.04)
- macos_window_utils (1.0.0):
- FlutterMacOS
- media_kit_libs_macos_video (1.0.4):
- FlutterMacOS
- media_kit_native_event_loop (1.0.0):
- FlutterMacOS
- media_kit_video (0.0.1):
- FlutterMacOS
- nanopb (2.30910.0):
- nanopb/decode (= 2.30910.0)
- nanopb/encode (= 2.30910.0)
@ -186,8 +180,6 @@ PODS:
- PromisesObjC (= 2.4.0)
- protocol_handler_macos (0.0.1):
- FlutterMacOS
- screen_brightness_macos (0.1.0):
- FlutterMacOS
- share_plus (0.0.1):
- FlutterMacOS
- shared_preferences_foundation (0.0.1):
@ -198,6 +190,9 @@ PODS:
- FlutterMacOS
- url_launcher_macos (0.0.1):
- FlutterMacOS
- video_player_avfoundation (0.0.1):
- Flutter
- FlutterMacOS
- wakelock_plus (0.0.1):
- FlutterMacOS
- WebRTC-SDK (125.6422.04)
@ -217,18 +212,15 @@ DEPENDENCIES:
- gal (from `Flutter/ephemeral/.symlinks/plugins/gal/darwin`)
- livekit_client (from `Flutter/ephemeral/.symlinks/plugins/livekit_client/macos`)
- macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/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`)
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
- pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- protocol_handler_macos (from `Flutter/ephemeral/.symlinks/plugins/protocol_handler_macos/macos`)
- screen_brightness_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_brightness_macos/macos`)
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/darwin`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
- video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`)
- wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
SPEC REPOS:
@ -280,12 +272,6 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/livekit_client/macos
macos_window_utils:
:path: Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos
media_kit_libs_macos_video:
: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:
:path: Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos
package_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
pasteboard:
@ -294,8 +280,6 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
protocol_handler_macos:
:path: Flutter/ephemeral/.symlinks/plugins/protocol_handler_macos/macos
screen_brightness_macos:
:path: Flutter/ephemeral/.symlinks/plugins/screen_brightness_macos/macos
share_plus:
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
shared_preferences_foundation:
@ -304,6 +288,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/darwin
url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
video_player_avfoundation:
:path: Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin
wakelock_plus:
:path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos
@ -335,9 +321,6 @@ SPEC CHECKSUMS:
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
livekit_client: 95f3b71e6545845aa658a6df0a3a62dcc3471d7c
macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663
media_kit_libs_macos_video: b3e2bbec2eef97c285f2b1baa7963c67c753fb82
media_kit_native_event_loop: 81fd5b45192b72f8b5b69eaf5b540f45777eb8d5
media_kit_video: c75b07f14d59706c775778e4dd47dd027de8d1e5
nanopb: 438bc412db1928dac798aa6fd75726007be04262
package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c
pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99
@ -345,11 +328,11 @@ SPEC CHECKSUMS:
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
protocol_handler_macos: d10a6c01d6373389ffd2278013ab4c47ed6d6daa
screen_brightness_macos: 2d6d3af2165592d9a55ffcd95b7550970e41ebda
share_plus: 36537c04ce0c3e3f5bd297ce4318b6d5ee5fd6cf
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399
video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269
WebRTC-SDK: c3d69a87e7185fad3568f6f3cff7c9ac5890acf3

View File

@ -206,6 +206,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.3"
chewie:
dependency: "direct main"
description:
name: chewie
sha256: "2243e41e79e865d426d9dd9c1a9624aa33c4ad11de2d0cd680f826e2cd30e879"
url: "https://pub.dev"
source: hosted
version: "1.8.3"
cli_util:
dependency: transitive
description:
@ -278,6 +286,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.5"
csslib:
dependency: transitive
description:
name: csslib
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
cupertino_icons:
dependency: "direct main"
description:
@ -534,6 +550,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.8.12"
firebase_performance:
dependency: "direct main"
description:
name: firebase_performance
sha256: "6d17133458b9627f15f278d6f71bebbbce885d393f3462b690e55deeb5c36b90"
url: "https://pub.dev"
source: hosted
version: "0.10.0+4"
firebase_performance_platform_interface:
dependency: transitive
description:
name: firebase_performance_platform_interface
sha256: "28dc0a70a3459fe51d1c1be5754803a9a0db0e210322ec7526f6ce42bf6ad83e"
url: "https://pub.dev"
source: hosted
version: "0.1.4+40"
firebase_performance_web:
dependency: transitive
description:
name: firebase_performance_web
sha256: db91d86b34280f5253d2913945fdd51d7114486584a298a7bedf1c4b2ab08f79
url: "https://pub.dev"
source: hosted
version: "0.1.6+12"
fixnum:
dependency: transitive
description:
@ -853,6 +893,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.5"
html:
dependency: transitive
description:
name: html
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
url: "https://pub.dev"
source: hosted
version: "0.15.4"
http:
dependency: transitive
description:
@ -1117,78 +1165,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.11.1"
media_kit:
dependency: "direct main"
description:
name: media_kit
sha256: "3289062540e3b8b9746e5c50d95bd78a9289826b7227e253dff806d002b9e67a"
url: "https://pub.dev"
source: hosted
version: "1.1.10+1"
media_kit_libs_android_video:
dependency: transitive
description:
name: media_kit_libs_android_video
sha256: "9dd8012572e4aff47516e55f2597998f0a378e3d588d0fad0ca1f11a53ae090c"
url: "https://pub.dev"
source: hosted
version: "1.3.6"
media_kit_libs_ios_video:
dependency: transitive
description:
name: media_kit_libs_ios_video
sha256: b5382994eb37a4564c368386c154ad70ba0cc78dacdd3fb0cd9f30db6d837991
url: "https://pub.dev"
source: hosted
version: "1.1.4"
media_kit_libs_linux:
dependency: transitive
description:
name: media_kit_libs_linux
sha256: e186891c31daa6bedab4d74dcdb4e8adfccc7d786bfed6ad81fe24a3b3010310
url: "https://pub.dev"
source: hosted
version: "1.1.3"
media_kit_libs_macos_video:
dependency: transitive
description:
name: media_kit_libs_macos_video
sha256: f26aa1452b665df288e360393758f84b911f70ffb3878032e1aabba23aa1032d
url: "https://pub.dev"
source: hosted
version: "1.1.4"
media_kit_libs_video:
dependency: "direct main"
description:
name: media_kit_libs_video
sha256: "3688e0c31482074578652bf038ce6301a5d21e1eda6b54fc3117ffeb4bdba067"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
media_kit_libs_windows_video:
dependency: transitive
description:
name: media_kit_libs_windows_video
sha256: "7bace5f35d9afcc7f9b5cdadb7541d2191a66bb3fc71bfa11c1395b3360f6122"
url: "https://pub.dev"
source: hosted
version: "1.0.9"
media_kit_native_event_loop:
dependency: transitive
description:
name: media_kit_native_event_loop
sha256: a605cf185499d14d58935b8784955a92a4bf0ff4e19a23de3d17a9106303930e
url: "https://pub.dev"
source: hosted
version: "1.0.8"
media_kit_video:
dependency: "direct main"
description:
name: media_kit_video
sha256: c048d11a19e379aebbe810647636e3fc6d18374637e2ae12def4ff8a4b99a882
url: "https://pub.dev"
source: hosted
version: "1.2.4"
meta:
dependency: transitive
description:
@ -1533,62 +1509,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.28.0"
safe_local_storage:
dependency: transitive
description:
name: safe_local_storage
sha256: ede4eb6cb7d88a116b3d3bf1df70790b9e2038bc37cb19112e381217c74d9440
url: "https://pub.dev"
source: hosted
version: "1.0.2"
screen_brightness:
dependency: transitive
description:
name: screen_brightness
sha256: ed8da4a4511e79422fc1aa88138e920e4008cd312b72cdaa15ccb426c0faaedd
url: "https://pub.dev"
source: hosted
version: "0.2.2+1"
screen_brightness_android:
dependency: transitive
description:
name: screen_brightness_android
sha256: "3df10961e3a9e968a5e076fe27e7f4741fa8a1d3950bdeb48cf121ed529d0caf"
url: "https://pub.dev"
source: hosted
version: "0.1.0+2"
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:
dependency: transitive
description:
name: screen_brightness_platform_interface
sha256: b211d07f0c96637a15fb06f6168617e18030d5d74ad03795dd8547a52717c171
url: "https://pub.dev"
source: hosted
version: "0.1.0"
screen_brightness_windows:
dependency: transitive
description:
name: screen_brightness_windows
sha256: "9261bf33d0fc2707d8cf16339ce25768100a65e70af0fcabaf032fc12408ba86"
url: "https://pub.dev"
source: hosted
version: "0.1.3"
sdp_transform:
dependency: transitive
description:
@ -1734,10 +1654,10 @@ packages:
dependency: transitive
description:
name: sqflite_common
sha256: c5e5b2a142a893a752cb36ae5888680248686725a54afceff31f9a3a76bc53c2
sha256: "7b41b6c3507854a159e24ae90a8e3e9cc01eb26a477c118d6dca065b5f55453e"
url: "https://pub.dev"
source: hosted
version: "2.5.4+1"
version: "2.5.4+2"
sqflite_common_ffi:
dependency: "direct dev"
description:
@ -1866,22 +1786,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.3.1"
universal_platform:
dependency: transitive
description:
name: universal_platform
sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
uri_parser:
dependency: transitive
description:
name: uri_parser
sha256: "6543c9fd86d2862fac55d800a43e67c0dcd1a41677cb69c2f8edfe73bbcf1835"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
url_launcher:
dependency: "direct main"
description:
@ -1962,6 +1866,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
video_player:
dependency: "direct main"
description:
name: video_player
sha256: e30df0d226c4ef82e2c150ebf6834b3522cf3f654d8e2f9419d376cdc071425d
url: "https://pub.dev"
source: hosted
version: "2.9.1"
video_player_android:
dependency: transitive
description:
name: video_player_android
sha256: "4de50df9ee786f5891d3281e1e633d7b142ef1acf47392592eb91cba5d355849"
url: "https://pub.dev"
source: hosted
version: "2.6.0"
video_player_avfoundation:
dependency: transitive
description:
name: video_player_avfoundation
sha256: d1e9a824f2b324000dc8fb2dcb2a3285b6c1c7c487521c63306cc5b394f68a7c
url: "https://pub.dev"
source: hosted
version: "2.6.1"
video_player_platform_interface:
dependency: transitive
description:
name: video_player_platform_interface
sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6"
url: "https://pub.dev"
source: hosted
version: "6.2.2"
video_player_web:
dependency: transitive
description:
name: video_player_web
sha256: "6dcdd298136523eaf7dfc31abaf0dfba9aa8a8dbc96670e87e9d42b6f2caf774"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
vm_service:
dependency: transitive
description:
@ -1970,14 +1914,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "14.2.4"
volume_controller:
dependency: transitive
description:
name: volume_controller
sha256: c71d4c62631305df63b72da79089e078af2659649301807fa746088f365cb48e
url: "https://pub.dev"
source: hosted
version: "2.0.8"
wakelock_plus:
dependency: "direct main"
description:

View File

@ -2,7 +2,7 @@ name: solian
description: "The Solar Network App"
publish_to: "none"
version: 1.2.1+14
version: 1.2.1+18
environment:
sdk: ">=3.3.4 <4.0.0"
@ -45,9 +45,6 @@ dependencies:
sqflite: ^2.3.3+1
protocol_handler: ^0.2.0
markdown: ^7.2.2
media_kit: ^1.1.10+1
media_kit_video: ^1.2.4
media_kit_libs_video: ^1.0.4
pasteboard: ^0.2.0
desktop_drop: ^0.4.4
badges: ^3.1.2
@ -70,6 +67,9 @@ dependencies:
collection: ^1.18.0
firebase_crashlytics: ^4.0.4
firebase_analytics: ^11.2.1
video_player: ^2.9.1
chewie: ^1.8.3
firebase_performance: ^0.10.0+4
dev_dependencies:
flutter_test:

View File

@ -15,12 +15,9 @@
#include <flutter_webrtc/flutter_web_r_t_c_plugin.h>
#include <gal/gal_plugin_c_api.h>
#include <livekit_client/live_kit_plugin.h>
#include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h>
#include <media_kit_video/media_kit_video_plugin_c_api.h>
#include <pasteboard/pasteboard_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <protocol_handler_windows/protocol_handler_windows_plugin_c_api.h>
#include <screen_brightness_windows/screen_brightness_windows_plugin.h>
#include <share_plus/share_plus_windows_plugin_c_api.h>
#include <url_launcher_windows/url_launcher_windows.h>
@ -43,18 +40,12 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("GalPluginCApi"));
LiveKitPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("LiveKitPlugin"));
MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi"));
MediaKitVideoPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi"));
PasteboardPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PasteboardPlugin"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
ProtocolHandlerWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ProtocolHandlerWindowsPluginCApi"));
ScreenBrightnessWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin"));
SharePlusWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
UrlLauncherWindowsRegisterWithRegistrar(

View File

@ -12,18 +12,14 @@ list(APPEND FLUTTER_PLUGIN_LIST
flutter_webrtc
gal
livekit_client
media_kit_libs_windows_video
media_kit_video
pasteboard
permission_handler_windows
protocol_handler_windows
screen_brightness_windows
share_plus
url_launcher_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
media_kit_native_event_loop
)
set(PLUGIN_BUNDLED_LIBRARIES)