Org publishers

This commit is contained in:
LittleSheep 2025-05-12 22:15:51 +08:00
parent 8efd8cd58e
commit ee8d502fc6
7 changed files with 106 additions and 41 deletions

View File

@ -35,6 +35,7 @@
"createPublisherHint": "To create posts, collections, etc.", "createPublisherHint": "To create posts, collections, etc.",
"editPublisher": "Edit Publisher", "editPublisher": "Edit Publisher",
"syncPublisher": "Use Account Data", "syncPublisher": "Use Account Data",
"syncPublisherRealm": "Use Realm Data",
"create": "Create", "create": "Create",
"update": "Update", "update": "Update",
"edit": "Edit", "edit": "Edit",
@ -179,5 +180,7 @@
"uploading": "Uploading", "uploading": "Uploading",
"uploadingProgress": "Uploading {} of {}", "uploadingProgress": "Uploading {} of {}",
"uploadAll": "Upload All", "uploadAll": "Upload All",
"stickerCopyPlaceholder": "Copy Placeholder" "stickerCopyPlaceholder": "Copy Placeholder",
"realmSelection": "Select a Realm",
"publisherIndividual": "Individual Publishers"
} }

View File

@ -165,14 +165,6 @@ PODS:
- super_native_extensions (0.0.1): - super_native_extensions (0.0.1):
- Flutter - Flutter
- SwiftyGif (5.4.5) - SwiftyGif (5.4.5)
- tencent_rtc_sdk (0.0.1):
- Flutter
- TXCustomBeautyProcesserPlugin (= 1.0.2)
- TXLiteAVSDK_Professional (~> 12.3.16995)
- TXCustomBeautyProcesserPlugin (1.0.2)
- TXLiteAVSDK_Professional (12.3.16995):
- TXLiteAVSDK_Professional/Professional (= 12.3.16995)
- TXLiteAVSDK_Professional/Professional (12.3.16995)
- url_launcher_ios (0.0.1): - url_launcher_ios (0.0.1):
- Flutter - Flutter
- volume_controller (0.0.1): - volume_controller (0.0.1):
@ -201,7 +193,6 @@ DEPENDENCIES:
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`) - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`)
- super_native_extensions (from `.symlinks/plugins/super_native_extensions/ios`) - super_native_extensions (from `.symlinks/plugins/super_native_extensions/ios`)
- tencent_rtc_sdk (from `.symlinks/plugins/tencent_rtc_sdk/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- volume_controller (from `.symlinks/plugins/volume_controller/ios`) - volume_controller (from `.symlinks/plugins/volume_controller/ios`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
@ -225,8 +216,6 @@ SPEC REPOS:
- SDWebImage - SDWebImage
- sqlite3 - sqlite3
- SwiftyGif - SwiftyGif
- TXCustomBeautyProcesserPlugin
- TXLiteAVSDK_Professional
EXTERNAL SOURCES: EXTERNAL SOURCES:
croppy: croppy:
@ -267,8 +256,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/sqlite3_flutter_libs/darwin" :path: ".symlinks/plugins/sqlite3_flutter_libs/darwin"
super_native_extensions: super_native_extensions:
:path: ".symlinks/plugins/super_native_extensions/ios" :path: ".symlinks/plugins/super_native_extensions/ios"
tencent_rtc_sdk:
:path: ".symlinks/plugins/tencent_rtc_sdk/ios"
url_launcher_ios: url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios" :path: ".symlinks/plugins/url_launcher_ios/ios"
volume_controller: volume_controller:
@ -313,9 +300,6 @@ SPEC CHECKSUMS:
sqlite3_flutter_libs: f6acaa2172e6bb3e2e70c771661905080e8ebcf2 sqlite3_flutter_libs: f6acaa2172e6bb3e2e70c771661905080e8ebcf2
super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4 super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
tencent_rtc_sdk: 13f066e69b87068b4cf3df90ad04097eb8773ae2
TXCustomBeautyProcesserPlugin: 099393b941cb40eda12b3a80bf6c0319957b1cfd
TXLiteAVSDK_Professional: 6e8fa18eeb0e52e6bbbcd97bab9ec9fbdc828df3
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12 volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556 wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556

View File

@ -83,7 +83,7 @@ final apiClientProvider = Provider<Dio>((ref) {
// ignore // ignore
} }
final userAgent = ref.watch(userAgentProvider); final userAgent = ref.read(userAgentProvider);
if (userAgent.value != null) { if (userAgent.value != null) {
options.headers['User-Agent'] = userAgent.value; options.headers['User-Agent'] = userAgent.value;
} }

View File

@ -1,17 +1,19 @@
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:croppy/croppy.dart' hide cropImage; import 'package:croppy/croppy.dart' hide cropImage;
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:dropdown_button2/dropdown_button2.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_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:island/models/file.dart';
import 'package:island/models/post.dart'; import 'package:island/models/post.dart';
import 'package:island/models/realm.dart';
import 'package:island/pods/config.dart'; import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/pods/userinfo.dart';
import 'package:island/route.gr.dart'; import 'package:island/route.gr.dart';
import 'package:island/screens/realm/realms.dart';
import 'package:island/services/file.dart'; import 'package:island/services/file.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
@ -173,15 +175,20 @@ class EditPublisherScreen extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final submitting = useState(false); final submitting = useState(false);
final picture = useState<SnCloudFile?>(null); final picture = useState<String?>(null);
final background = useState<SnCloudFile?>(null); final background = useState<String?>(null);
void setPicture(String position) async { void setPicture(String position) async {
showLoadingModal(context);
var result = await ref var result = await ref
.read(imagePickerProvider) .read(imagePickerProvider)
.pickImage(source: ImageSource.gallery); .pickImage(source: ImageSource.gallery);
if (result == null) return; if (result == null) {
if (context.mounted) hideLoadingModal(context);
return;
}
if (!context.mounted) return; if (!context.mounted) return;
hideLoadingModal(context);
result = await cropImage( result = await cropImage(
context, context,
image: result, image: result,
@ -194,6 +201,7 @@ class EditPublisherScreen extends HookConsumerWidget {
); );
if (result == null) return; if (result == null) return;
if (!context.mounted) return; if (!context.mounted) return;
showLoadingModal(context);
submitting.value = true; submitting.value = true;
try { try {
@ -220,14 +228,15 @@ class EditPublisherScreen extends HookConsumerWidget {
} }
switch (position) { switch (position) {
case 'picture': case 'picture':
picture.value = cloudFile; picture.value = cloudFile.id;
case 'background': case 'background':
background.value = cloudFile; background.value = cloudFile.id;
} }
} catch (err) { } catch (err) {
showErrorAlert(err); showErrorAlert(err);
} finally { } finally {
submitting.value = false; submitting.value = false;
if (context.mounted) hideLoadingModal(context);
} }
} }
@ -242,10 +251,13 @@ class EditPublisherScreen extends HookConsumerWidget {
); );
final bioController = useTextEditingController(text: publisher.value?.bio); final bioController = useTextEditingController(text: publisher.value?.bio);
final joinedRealms = ref.watch(realmsJoinedProvider);
final currentRealm = useState<SnRealm?>(null);
useEffect(() { useEffect(() {
if (publisher.value != null) { if (publisher.value != null) {
picture.value = publisher.value!.picture; picture.value = publisher.value!.pictureId;
background.value = publisher.value!.background; background.value = publisher.value!.backgroundId;
nameController.text = publisher.value!.name; nameController.text = publisher.value!.name;
nickController.text = publisher.value!.nick; nickController.text = publisher.value!.nick;
bioController.text = publisher.value!.bio; bioController.text = publisher.value!.bio;
@ -265,8 +277,8 @@ class EditPublisherScreen extends HookConsumerWidget {
'name': nameController.text, 'name': nameController.text,
'nick': nickController.text, 'nick': nickController.text,
'bio': bioController.text, 'bio': bioController.text,
'picture_id': picture.value?.id, 'picture_id': picture.value,
'background_id': background.value?.id, 'background_id': background.value,
}, },
options: Options(method: name == null ? 'POST' : 'PATCH'), options: Options(method: name == null ? 'POST' : 'PATCH'),
); );
@ -287,6 +299,55 @@ class EditPublisherScreen extends HookConsumerWidget {
), ),
body: Column( body: Column(
children: [ children: [
DropdownButtonHideUnderline(
child: DropdownButton2<SnRealm?>(
isExpanded: true,
hint: Text('realmSelection').tr(),
value: currentRealm.value,
items: [
DropdownMenuItem<SnRealm?>(
value: null,
child: Row(
spacing: 12,
children: [
CircleAvatar(radius: 16, child: Icon(Symbols.person)),
Text('publisherIndividual').tr(),
],
),
),
...joinedRealms.when(
data:
(realms) =>
realms
.map(
(realm) => DropdownMenuItem<SnRealm?>(
value: realm,
child: Row(
spacing: 12,
children: [
ProfilePictureWidget(
fileId: realm.pictureId,
fallbackIcon: Symbols.workspaces,
radius: 16,
),
Text(realm.name),
],
),
),
)
.toList(),
loading: () => [],
error: (_, __) => [],
),
],
onChanged: (SnRealm? value) {
currentRealm.value = value;
},
buttonStyleData: ButtonStyleData(
padding: const EdgeInsets.only(left: 4, right: 16),
),
),
),
AspectRatio( AspectRatio(
aspectRatio: 16 / 7, aspectRatio: 16 / 7,
child: Stack( child: Stack(
@ -298,8 +359,8 @@ class EditPublisherScreen extends HookConsumerWidget {
color: Theme.of(context).colorScheme.surfaceContainerHigh, color: Theme.of(context).colorScheme.surfaceContainerHigh,
child: child:
background.value != null background.value != null
? CloudFileWidget( ? CloudImageWidget(
item: background.value!, fileId: background.value!,
fit: BoxFit.cover, fit: BoxFit.cover,
) )
: const SizedBox.shrink(), : const SizedBox.shrink(),
@ -313,7 +374,7 @@ class EditPublisherScreen extends HookConsumerWidget {
bottom: -32, bottom: -32,
child: GestureDetector( child: GestureDetector(
child: ProfilePictureWidget( child: ProfilePictureWidget(
fileId: picture.value?.id, fileId: picture.value,
radius: 40, radius: 40,
), ),
onTap: () { onTap: () {
@ -360,19 +421,32 @@ class EditPublisherScreen extends HookConsumerWidget {
children: [ children: [
TextButton.icon( TextButton.icon(
onPressed: () { onPressed: () {
final user = ref.watch(userInfoProvider); if (currentRealm.value == null) {
nameController.text = user.value!.name; final user = ref.watch(userInfoProvider);
nickController.text = user.value!.nick; nameController.text = user.value!.name;
bioController.text = user.value!.profile.bio ?? ''; nickController.text = user.value!.nick;
picture.value = user.value!.profile.picture; bioController.text = user.value!.profile.bio ?? '';
background.value = user.value!.profile.background; picture.value = user.value!.profile.pictureId;
background.value = user.value!.profile.backgroundId;
} else {
nameController.text = currentRealm.value!.slug;
nickController.text = currentRealm.value!.name;
bioController.text = currentRealm.value!.description;
picture.value = currentRealm.value!.pictureId;
background.value = currentRealm.value!.backgroundId;
}
}, },
label: Text('syncPublisher'.tr()), label:
Text(
currentRealm.value == null
? 'syncPublisher'
: 'syncPublisherRealm',
).tr(),
icon: const Icon(Symbols.link), icon: const Icon(Symbols.link),
), ),
TextButton.icon( TextButton.icon(
onPressed: submitting.value ? null : performAction, onPressed: submitting.value ? null : performAction,
label: Text('saveChanges'.tr()), label: Text(name == null ? 'create' : 'saveChanges').tr(),
icon: const Icon(Symbols.save), icon: const Icon(Symbols.save),
), ),
], ],

View File

@ -32,6 +32,7 @@ class UpdateProfileScreen extends HookConsumerWidget {
.pickImage(source: ImageSource.gallery); .pickImage(source: ImageSource.gallery);
if (result == null) return; if (result == null) return;
if (!context.mounted) return; if (!context.mounted) return;
hideLoadingModal(context);
result = await cropImage( result = await cropImage(
context, context,
image: result, image: result,
@ -47,6 +48,7 @@ class UpdateProfileScreen extends HookConsumerWidget {
return; return;
} }
if (!context.mounted) return; if (!context.mounted) return;
showLoadingModal(context);
submitting.value = true; submitting.value = true;
try { try {

View File

@ -254,7 +254,7 @@ class EditChatScreen extends HookConsumerWidget {
return; return;
} }
if (!context.mounted) return; if (!context.mounted) return;
hideLoadingModal(context);
result = await cropImage( result = await cropImage(
context, context,
image: result, image: result,
@ -267,6 +267,7 @@ class EditChatScreen extends HookConsumerWidget {
); );
if (result == null) return; if (result == null) return;
if (!context.mounted) return; if (!context.mounted) return;
showLoadingModal(context);
submitting.value = true; submitting.value = true;
try { try {

View File

@ -165,6 +165,7 @@ class EditRealmScreen extends HookConsumerWidget {
.pickImage(source: ImageSource.gallery); .pickImage(source: ImageSource.gallery);
if (result == null) return; if (result == null) return;
if (!context.mounted) return; if (!context.mounted) return;
hideLoadingModal(context);
result = await cropImage( result = await cropImage(
context, context,
image: result, image: result,
@ -180,7 +181,7 @@ class EditRealmScreen extends HookConsumerWidget {
return; return;
} }
if (!context.mounted) return; if (!context.mounted) return;
showLoadingModal(context);
submitting.value = true; submitting.value = true;
try { try {
final baseUrl = ref.watch(serverUrlProvider); final baseUrl = ref.watch(serverUrlProvider);