🐛 Dozens of bug fixes

This commit is contained in:
2025-07-20 02:31:50 +08:00
parent 903008d397
commit 18db50d80c
33 changed files with 359 additions and 318 deletions

View File

@@ -698,5 +698,9 @@
"postForwardingTo": "Forwarding to",
"postReplyingTo": "Replying to",
"postEditing": "You are editing an existing post",
"postArticle": "Article"
"postArticle": "Article",
"aboutDeviceName": "Device Name",
"aboutDeviceIdentifier": "Device Identifier",
"donate": "Donate",
"donateDescription": "Support us to continue developing the Solar Network and keep the server up and running."
}

View File

@@ -1,18 +1,19 @@
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/network.dart';
import 'package:island/services/notify.dart';
import 'package:island/services/udid.native.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:url_launcher/url_launcher_string.dart';
class AboutScreen extends ConsumerStatefulWidget {
const AboutScreen({super.key});
@@ -168,34 +169,16 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
_buildInfoItem(
context,
icon: Symbols.label,
label: 'Device Name',
label: 'aboutDeviceName'.tr(),
value: _deviceInfo?.data['name'],
),
_buildInfoItem(
context,
icon: Symbols.fingerprint,
label: 'Device Identifier',
label: 'aboutDeviceIdentifier'.tr(),
value: _deviceUdid ?? 'N/A',
copyable: true,
),
const Divider(height: 1),
_buildListTile(
context,
icon: Symbols.notifications_active,
title: 'Reactivate Push Notifications',
onTap: () async {
showLoadingModal(context);
try {
await subscribePushNotification(
ref.watch(apiClientProvider),
);
} catch (err) {
showErrorAlert(err);
} finally {
if (context.mounted) hideLoadingModal(context);
}
},
),
],
),
@@ -266,6 +249,18 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
'https://github.com/Solsynth/Solian/blob/v3/LICENSE.txt',
),
),
if (kIsWeb || !(Platform.isMacOS || Platform.isIOS))
_buildListTile(
context,
icon: Symbols.favorite,
title: 'donate'.tr(),
subtitle: 'donateDescription'.tr(),
onTap: () {
launchUrlString(
'https://afdian.com/@littlesheep',
);
},
),
],
),

View File

@@ -106,7 +106,7 @@ class AuthFactorSheet extends HookConsumerWidget {
showLoadingModal(context);
final client = ref.read(apiClientProvider);
await client.post(
'/accounts/me/factors/${factor.id}/enable',
'/id/accounts/me/factors/${factor.id}/enable',
data: jsonEncode(password),
);
if (context.mounted) Navigator.pop(context, true);
@@ -193,7 +193,7 @@ class AuthFactorNewSheet extends HookConsumerWidget {
showLoadingModal(context);
final apiClient = ref.read(apiClientProvider);
final resp = await apiClient.post(
'/accounts/me/factors',
'/id/accounts/me/factors',
data: {'type': factorType.value, 'secret': secretController.text},
);
final factor = SnAuthFactor.fromJson(resp.data);

View File

@@ -174,7 +174,7 @@ class AccountConnectionNewSheet extends HookConsumerWidget {
if (context.mounted) showLoadingModal(context);
await client.post(
'/auth/connect/apple/mobile',
'/id/auth/connect/apple/mobile',
data: {
'identity_token': credential.identityToken!,
'authorization_code': credential.authorizationCode,
@@ -336,7 +336,7 @@ class AccountConnectionsSheet extends HookConsumerWidget {
try {
final client = ref.read(apiClientProvider);
await client.delete(
'/accounts/me/connections/${connection.id}',
'/id/accounts/me/connections/${connection.id}',
);
ref.invalidate(accountConnectionsProvider);
return true;

View File

@@ -160,7 +160,7 @@ class ContactMethodNewSheet extends HookConsumerWidget {
showLoadingModal(context);
final apiClient = ref.read(apiClientProvider);
await apiClient.post(
'/accounts/me/contacts',
'/id/accounts/me/contacts',
data: {'type': contactType.value, 'content': contentController.text},
);
if (context.mounted) {

View File

@@ -39,14 +39,14 @@ Future<SnAccount> account(Ref ref, String uname) async {
}
}
final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get("/accounts/$uname");
final resp = await apiClient.get("/id/accounts/$uname");
return SnAccount.fromJson(resp.data);
}
@riverpod
Future<List<SnAccountBadge>> accountBadges(Ref ref, String uname) async {
final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get("/accounts/$uname/badges");
final resp = await apiClient.get("/id/accounts/$uname/badges");
return List<SnAccountBadge>.from(
resp.data.map((x) => SnAccountBadge.fromJson(x)),
);
@@ -78,7 +78,7 @@ Future<SnChatRoom?> accountDirectChat(Ref ref, String uname) async {
final account = await ref.watch(accountProvider(uname).future);
final apiClient = ref.watch(apiClientProvider);
try {
final resp = await apiClient.get("/chat/direct/${account.id}");
final resp = await apiClient.get("/sphere/chat/direct/${account.id}");
return SnChatRoom.fromJson(resp.data);
} catch (err) {
if (err is DioException && err.response?.statusCode == 404) {
@@ -95,7 +95,7 @@ Future<SnRelationship?> accountRelationship(Ref ref, String uname) async {
final account = await ref.watch(accountProvider(uname).future);
final apiClient = ref.watch(apiClientProvider);
try {
final resp = await apiClient.get("/relationships/${account.id}");
final resp = await apiClient.get("/id/relationships/${account.id}");
return SnRelationship.fromJson(resp.data);
} catch (err) {
if (err is DioException && err.response?.statusCode == 404) {
@@ -174,7 +174,7 @@ class AccountProfileScreen extends HookConsumerWidget {
try {
final client = ref.watch(apiClientProvider);
final resp = await client.post(
'/chat/direct',
'/sphere/chat/direct',
data: {'related_user_id': account.value!.id},
);
final chat = SnChatRoom.fromJson(resp.data);

View File

@@ -6,7 +6,7 @@ part of 'profile.dart';
// RiverpodGenerator
// **************************************************************************
String _$accountHash() => r'd2b0579617e6264452d98f47f695a9cdf45b24ec';
String _$accountHash() => r'ce7264a04f69e32a5cb07bc10ca5fa47ae1fddaa';
/// Copied from Dart SDK
class _SystemHash {
@@ -145,7 +145,7 @@ class _AccountProviderElement
String get uname => (origin as AccountProvider).uname;
}
String _$accountBadgesHash() => r'4bfe5fb0d6ac0d4cde4563460bde289289188f6d';
String _$accountBadgesHash() => r'1de05e122c23ff2c6ac6d318977165761e2ad177';
/// See also [accountBadges].
@ProviderFor(accountBadges)
@@ -395,7 +395,7 @@ class _AccountAppbarForcegroundColorProviderElement
String get uname => (origin as AccountAppbarForcegroundColorProvider).uname;
}
String _$accountDirectChatHash() => r'3d28c8ba8079159f724fe3cd47bbe00db55cedcc';
String _$accountDirectChatHash() => r'149ea3a3730672cfbbb8c16fe1f2caa0bb9f0e17';
/// See also [accountDirectChat].
@ProviderFor(accountDirectChat)
@@ -517,7 +517,7 @@ class _AccountDirectChatProviderElement
}
String _$accountRelationshipHash() =>
r'0be2420e1f6a65b8dcead9617191471924aaf232';
r'9a3a4e8c6c6706f73df95feccb86736fcad33f30';
/// See also [accountRelationship].
@ProviderFor(accountRelationship)

View File

@@ -42,7 +42,7 @@ class RelationshipListNotifier extends _$RelationshipListNotifier
final take = 20;
final response = await client.get(
'/relationships',
'/id/relationships',
queryParameters: {'offset': offset, 'take': take},
);
@@ -235,7 +235,7 @@ class RelationshipScreen extends HookConsumerWidget {
submitting.value = true;
final client = ref.read(apiClientProvider);
await client.post(
'/relationships/${relationship.accountId}/friends/${isAccept ? 'accept' : 'decline'}',
'/id/relationships/${relationship.accountId}/friends/${isAccept ? 'accept' : 'decline'}',
);
relationshipNotifier.forceRefresh();
if (!context.mounted) return;
@@ -262,7 +262,7 @@ class RelationshipScreen extends HookConsumerWidget {
) async {
final client = ref.read(apiClientProvider);
await client.patch(
'/relationships/${relationship.accountId}',
'/id/relationships/${relationship.accountId}',
data: {'status': newStatus},
);
relationshipNotifier.forceRefresh();

View File

@@ -27,7 +27,7 @@ final sentFriendRequestProvider =
typedef SentFriendRequestRef =
AutoDisposeFutureProviderRef<List<SnRelationship>>;
String _$relationshipListNotifierHash() =>
r'560410cba6e4c26affd91aa86b3666319bd31f24';
r'0a134ce69489a4f2002d2223853855b6f22e4e9f';
/// See also [RelationshipListNotifier].
@ProviderFor(RelationshipListNotifier)

View File

@@ -298,6 +298,7 @@ class ChatListScreen extends HookConsumerWidget {
),
onPressed: () {
showModalBottomSheet(
useRootNavigator: true,
isScrollControlled: true,
context: context,
builder: (context) => const _ChatInvitesSheet(),
@@ -608,127 +609,129 @@ class EditChatScreen extends HookConsumerWidget {
title: Text(id == null ? 'createChatRoom' : 'editChatRoom').tr(),
leading: const PageBackButton(),
),
body: Column(
children: [
RealmSelectionDropdown(
value: currentRealm.value,
realms: joinedRealms.when(
data: (realms) => realms,
loading: () => [],
error: (_, _) => [],
body: SingleChildScrollView(
child: Column(
children: [
RealmSelectionDropdown(
value: currentRealm.value,
realms: joinedRealms.when(
data: (realms) => realms,
loading: () => [],
error: (_, _) => [],
),
onChanged: (SnRealm? value) {
currentRealm.value = value;
},
isLoading: joinedRealms.isLoading,
error: joinedRealms.error?.toString(),
),
onChanged: (SnRealm? value) {
currentRealm.value = value;
},
isLoading: joinedRealms.isLoading,
error: joinedRealms.error?.toString(),
),
AspectRatio(
aspectRatio: 16 / 7,
child: Stack(
clipBehavior: Clip.none,
fit: StackFit.expand,
children: [
GestureDetector(
child: Container(
color: Theme.of(context).colorScheme.surfaceContainerHigh,
child:
background.value != null
? CloudFileWidget(
item: background.value!,
fit: BoxFit.cover,
)
: const SizedBox.shrink(),
),
onTap: () {
setPicture('background');
},
),
Positioned(
left: 20,
bottom: -32,
child: GestureDetector(
child: ProfilePictureWidget(
fileId: picture.value?.id,
radius: 40,
fallbackIcon: Symbols.group,
AspectRatio(
aspectRatio: 16 / 7,
child: Stack(
clipBehavior: Clip.none,
fit: StackFit.expand,
children: [
GestureDetector(
child: Container(
color: Theme.of(context).colorScheme.surfaceContainerHigh,
child:
background.value != null
? CloudFileWidget(
item: background.value!,
fit: BoxFit.cover,
)
: const SizedBox.shrink(),
),
onTap: () {
setPicture('picture');
setPicture('background');
},
),
),
],
Positioned(
left: 20,
bottom: -32,
child: GestureDetector(
child: ProfilePictureWidget(
fileId: picture.value?.id,
radius: 40,
fallbackIcon: Symbols.group,
),
onTap: () {
setPicture('picture');
},
),
),
],
),
).padding(bottom: 32),
Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
controller: nameController,
decoration: const InputDecoration(labelText: 'Name'),
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const SizedBox(height: 16),
TextFormField(
controller: descriptionController,
decoration: const InputDecoration(
labelText: 'Description',
alignLabelWithHint: true,
),
minLines: 3,
maxLines: null,
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const SizedBox(height: 16),
Card(
margin: EdgeInsets.zero,
child: Column(
children: [
CheckboxListTile(
secondary: const Icon(Symbols.public),
title: Text('publicChat').tr(),
subtitle: Text('publicChatDescription').tr(),
value: isPublic.value,
onChanged: (value) {
isPublic.value = value ?? true;
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
CheckboxListTile(
secondary: const Icon(Symbols.travel_explore),
title: Text('communityChat').tr(),
subtitle: Text('communityChatDescription').tr(),
value: isCommunity.value,
onChanged: (value) {
isCommunity.value = value ?? false;
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
],
),
),
const SizedBox(height: 16),
Align(
alignment: Alignment.centerRight,
child: TextButton.icon(
onPressed: submitting.value ? null : performAction,
label: const Text('Save'),
icon: const Icon(Symbols.save),
),
),
],
).padding(all: 24),
),
).padding(bottom: 32),
Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
controller: nameController,
decoration: const InputDecoration(labelText: 'Name'),
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const SizedBox(height: 16),
TextFormField(
controller: descriptionController,
decoration: const InputDecoration(
labelText: 'Description',
alignLabelWithHint: true,
),
minLines: 3,
maxLines: null,
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const SizedBox(height: 16),
Card(
margin: EdgeInsets.zero,
child: Column(
children: [
CheckboxListTile(
secondary: const Icon(Symbols.public),
title: Text('publicChat').tr(),
subtitle: Text('publicChatDescription').tr(),
value: isPublic.value,
onChanged: (value) {
isPublic.value = value ?? true;
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
CheckboxListTile(
secondary: const Icon(Symbols.travel_explore),
title: Text('communityChat').tr(),
subtitle: Text('communityChatDescription').tr(),
value: isCommunity.value,
onChanged: (value) {
isCommunity.value = value ?? false;
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
],
),
),
const SizedBox(height: 16),
Align(
alignment: Alignment.centerRight,
child: TextButton.icon(
onPressed: submitting.value ? null : performAction,
label: const Text('Save'),
icon: const Icon(Symbols.save),
),
),
],
).padding(all: 24),
),
],
],
),
),
);
}

View File

@@ -389,7 +389,10 @@ class _ChatRoomActionMenu extends HookConsumerWidget {
if ((chatIdentity.value?.role ?? 0) >= 50)
PopupMenuItem(
onTap: () {
context.pushReplacement('/sphere/chat/$id/edit');
context.pushReplacementNamed(
'chatEdit',
pathParameters: {'id': id},
);
},
child: Row(
children: [
@@ -502,7 +505,7 @@ class ChatMemberNotifier extends StateNotifier<ChatRoomMemberState> {
try {
final response = await _apiClient.get(
'/chat/$roomId/members',
'/sphere/chat/$roomId/members',
queryParameters: {'offset': offset, 'take': take},
);
@@ -540,7 +543,7 @@ class ChatMemberListNotifier extends _$ChatMemberListNotifier
final apiClient = ref.watch(apiClientProvider);
final response = await apiClient.get(
'/chat/$roomId/members',
'/sphere/chat/$roomId/members',
queryParameters: {'offset': offset, 'take': take},
);
@@ -592,7 +595,7 @@ class _ChatMemberListSheet extends HookConsumerWidget {
try {
final apiClient = ref.watch(apiClientProvider);
await apiClient.post(
'/chat/invites/$roomId',
'/sphere/chat/invites/$roomId',
data: {'related_user_id': result.id, 'role': 0},
);
// Refresh both providers
@@ -846,7 +849,7 @@ class _ChatMemberRoleSheet extends HookConsumerWidget {
final apiClient = ref.read(apiClientProvider);
await apiClient.patch(
'/chat/$roomId/members/${member.accountId}/role',
'/sphere/chat/$roomId/members/${member.accountId}/role',
data: newRole,
);

View File

@@ -414,7 +414,7 @@ final publisherInvitesProvider =
typedef PublisherInvitesRef =
AutoDisposeFutureProviderRef<List<SnPublisherMember>>;
String _$publisherMemberListNotifierHash() =>
r'237e8f39c9757a6cbdff817853c697539242ad2a';
r'b4afd5d591a6f3d29f1b45fb1b6d17cb34f3f11b';
abstract class _$PublisherMemberListNotifier
extends

View File

@@ -6,7 +6,7 @@ part of 'stickers.dart';
// RiverpodGenerator
// **************************************************************************
String _$stickerPackHash() => r'4f70d26e695ba1d8c7273d12730f77da79361733';
String _$stickerPackHash() => r'71ef84471237c8191918095094bdfc87d3920e77';
/// Copied from Dart SDK
class _SystemHash {
@@ -148,7 +148,7 @@ class _StickerPackProviderElement
}
String _$stickerPacksNotifierHash() =>
r'dc0cc4ec27fdd6d5da28f982ff10c852f8107a18';
r'0a8edcf9c35396c411f1214f5e77b1e8fac6a3e6';
abstract class _$StickerPacksNotifier
extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnStickerPack>> {

View File

@@ -6,7 +6,7 @@ part of 'hub.dart';
// RiverpodGenerator
// **************************************************************************
String _$developerStatsHash() => r'783398cbde09c3d956c3e20b02a1cebd1f8ab748';
String _$developerStatsHash() => r'baa708f3586e8987e221cc8ab825d759658c0f55';
/// Copied from Dart SDK
class _SystemHash {
@@ -149,7 +149,7 @@ class _DeveloperStatsProviderElement
String? get uname => (origin as DeveloperStatsProvider).uname;
}
String _$developersHash() => r'f52639d3c21aafbf235c8ae33f35448baf2989a1';
String _$developersHash() => r'f11335fdf553c661110281edeec70ef89c64727d';
/// See also [developers].
@ProviderFor(developers)

View File

@@ -39,7 +39,7 @@ class ArticlesListNotifier extends _$ArticlesListNotifier
try {
final response = await client.get(
'/feeds/articles',
'/sphere/feeds/articles',
queryParameters: queryParams,
);

View File

@@ -82,7 +82,7 @@ class NotificationListNotifier extends _$NotificationListNotifier
final queryParams = {'offset': offset, 'take': _pageSize};
final response = await client.get(
'/notifications',
'/pusher/notifications',
queryParameters: queryParams,
);
final total = int.parse(response.headers.value('X-Total') ?? '0');

View File

@@ -28,7 +28,7 @@ final notificationUnreadCountNotifierProvider =
typedef _$NotificationUnreadCountNotifier = AutoDisposeAsyncNotifier<int>;
String _$notificationListNotifierHash() =>
r'934a47bc2ce9e75699a4f53e2169470fd0c04a53';
r'5099466db475bbcf1ab6b514eb072f1dc4c6f930';
/// See also [NotificationListNotifier].
@ProviderFor(NotificationListNotifier)

View File

@@ -50,7 +50,7 @@ class PostSearchNotifier
final offset = cursor == null ? 0 : int.parse(cursor);
final response = await client.get(
'/posts/search',
'/sphere/posts/search',
queryParameters: {
'query': _currentQuery,
'offset': offset,

View File

@@ -28,7 +28,7 @@ part 'pub_profile.g.dart';
@riverpod
Future<SnPublisher> publisher(Ref ref, String uname) async {
final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get("/publishers/$uname");
final resp = await apiClient.get("/sphere/publishers/$uname");
return SnPublisher.fromJson(resp.data);
}
@@ -37,7 +37,7 @@ Future<List<SnAccountBadge>> publisherBadges(Ref ref, String pubName) async {
final pub = await ref.watch(publisherProvider(pubName).future);
if (pub.type != 0 || pub.account == null) return [];
final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get("/accounts/${pub.account!.name}/badges");
final resp = await apiClient.get("/id/accounts/${pub.account!.name}/badges");
return List<SnAccountBadge>.from(
resp.data.map((x) => SnAccountBadge.fromJson(x)),
);
@@ -49,7 +49,7 @@ Future<SnSubscriptionStatus> publisherSubscriptionStatus(
String pubName,
) async {
final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get("/publishers/$pubName/subscription");
final resp = await apiClient.get("/sphere/publishers/$pubName/subscription");
return SnSubscriptionStatus.fromJson(resp.data);
}
@@ -188,7 +188,10 @@ class PublisherProfileScreen extends HookConsumerWidget {
onTap: () {
Navigator.pop(context, true);
if (data.account?.name != null) {
context.pushNamed('accountProfile', pathParameters: {'name': data.account!.name});
context.pushNamed(
'accountProfile',
pathParameters: {'name': data.account!.name},
);
}
},
),

View File

@@ -399,7 +399,7 @@ class _RealmChatRoomsProviderElement
}
String _$realmMemberListNotifierHash() =>
r'b2e3eefc62a597f45df9470b2058fdda62f8853f';
r'022bcef5a90cbae05ff23b937851afc3ef913d42';
abstract class _$RealmMemberListNotifier
extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnRealmMember>> {

View File

@@ -41,7 +41,6 @@ class RealmListScreen extends HookConsumerWidget {
final realmInvites = ref.watch(realmInvitesProvider);
return AppScaffold(
extendBody: false, // Prevent conflicts with tabs navigation
noBackground: false,
appBar: AppBar(
title: const Text('realms').tr(),
@@ -70,6 +69,7 @@ class RealmListScreen extends HookConsumerWidget {
showModalBottomSheet(
context: context,
isScrollControlled: true,
useRootNavigator: true,
builder: (_) => const _RealmInviteSheet(),
);
},
@@ -279,128 +279,131 @@ class EditRealmScreen extends HookConsumerWidget {
}
return AppScaffold(
noBackground: false,
appBar: AppBar(
title: Text(slug == null ? 'createRealm'.tr() : 'editRealm'.tr()),
leading: const PageBackButton(),
),
body: Column(
children: [
AspectRatio(
aspectRatio: 16 / 7,
child: Stack(
clipBehavior: Clip.none,
fit: StackFit.expand,
children: [
GestureDetector(
child: Container(
color: Theme.of(context).colorScheme.surfaceContainerHigh,
child:
background.value != null
? CloudFileWidget(
item: background.value!,
fit: BoxFit.cover,
)
: const SizedBox.shrink(),
),
onTap: () {
setPicture('background');
},
),
Positioned(
left: 20,
bottom: -32,
child: GestureDetector(
child: ProfilePictureWidget(
fileId: picture.value?.id,
radius: 40,
fallbackIcon: Symbols.group,
body: SingleChildScrollView(
child: Column(
children: [
AspectRatio(
aspectRatio: 16 / 7,
child: Stack(
clipBehavior: Clip.none,
fit: StackFit.expand,
children: [
GestureDetector(
child: Container(
color: Theme.of(context).colorScheme.surfaceContainerHigh,
child:
background.value != null
? CloudFileWidget(
item: background.value!,
fit: BoxFit.cover,
)
: const SizedBox.shrink(),
),
onTap: () {
setPicture('picture');
setPicture('background');
},
),
),
],
Positioned(
left: 20,
bottom: -32,
child: GestureDetector(
child: ProfilePictureWidget(
fileId: picture.value?.id,
radius: 40,
fallbackIcon: Symbols.group,
),
onTap: () {
setPicture('picture');
},
),
),
],
),
).padding(bottom: 32),
Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
controller: slugController,
decoration: InputDecoration(
labelText: 'slug'.tr(),
helperText: 'slugHint'.tr(),
),
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const SizedBox(height: 16),
TextFormField(
controller: nameController,
decoration: InputDecoration(labelText: 'name'.tr()),
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const SizedBox(height: 16),
TextFormField(
controller: descriptionController,
decoration: InputDecoration(
labelText: 'description'.tr(),
alignLabelWithHint: true,
),
minLines: 3,
maxLines: null,
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const SizedBox(height: 16),
Card(
margin: EdgeInsets.zero,
child: Column(
children: [
CheckboxListTile(
secondary: const Icon(Symbols.public),
title: Text('publicRealm').tr(),
subtitle: Text('publicRealmDescription').tr(),
value: isPublic.value,
onChanged: (value) {
isPublic.value = value ?? true;
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
CheckboxListTile(
secondary: const Icon(Symbols.travel_explore),
title: Text('communityRealm').tr(),
subtitle: Text('communityRealmDescription').tr(),
value: isCommunity.value,
onChanged: (value) {
isCommunity.value = value ?? false;
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
],
),
),
const SizedBox(height: 16),
Align(
alignment: Alignment.centerRight,
child: TextButton.icon(
onPressed: submitting.value ? null : performAction,
label: Text('saveChanges'.tr()),
icon: const Icon(Symbols.save),
),
),
],
).padding(all: 24),
),
).padding(bottom: 32),
Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
controller: slugController,
decoration: InputDecoration(
labelText: 'slug'.tr(),
helperText: 'slugHint'.tr(),
),
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const SizedBox(height: 16),
TextFormField(
controller: nameController,
decoration: InputDecoration(labelText: 'name'.tr()),
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const SizedBox(height: 16),
TextFormField(
controller: descriptionController,
decoration: InputDecoration(
labelText: 'description'.tr(),
alignLabelWithHint: true,
),
minLines: 3,
maxLines: null,
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const SizedBox(height: 16),
Card(
margin: EdgeInsets.zero,
child: Column(
children: [
CheckboxListTile(
secondary: const Icon(Symbols.public),
title: Text('publicRealm').tr(),
subtitle: Text('publicRealmDescription').tr(),
value: isPublic.value,
onChanged: (value) {
isPublic.value = value ?? true;
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
CheckboxListTile(
secondary: const Icon(Symbols.travel_explore),
title: Text('communityRealm').tr(),
subtitle: Text('communityRealmDescription').tr(),
value: isCommunity.value,
onChanged: (value) {
isCommunity.value = value ?? false;
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
],
),
),
const SizedBox(height: 16),
Align(
alignment: Alignment.centerRight,
child: TextButton.icon(
onPressed: submitting.value ? null : performAction,
label: Text('saveChanges'.tr()),
icon: const Icon(Symbols.save),
),
),
],
).padding(all: 24),
),
],
],
),
),
);
}

View File

@@ -55,9 +55,9 @@ class TabsScreen extends HookConsumerWidget {
final routes = ['/', '/chat', '/realms', '/account'];
int getCurrentIndex() {
if (currentLocation.startsWith('/sphere/chat')) return 1;
if (currentLocation.startsWith('/sphere/realms')) return 2;
if (currentLocation.startsWith('/id/account')) return 3;
if (currentLocation.startsWith('/chat')) return 1;
if (currentLocation.startsWith('/realms')) return 2;
if (currentLocation.startsWith('/account')) return 3;
return 0; // Default to explore
}

View File

@@ -25,7 +25,7 @@ final walletCurrentProvider = AutoDisposeFutureProvider<SnWallet?>.internal(
// ignore: unused_element
typedef WalletCurrentRef = AutoDisposeFutureProviderRef<SnWallet?>;
String _$transactionListNotifierHash() =>
r'148ffb0ee9e3be3b92de432f314d8ee2f09e9a24';
r'7b777cd44f3351f68f7bd1dd76bfe8b388381bdb';
/// See also [TransactionListNotifier].
@ProviderFor(TransactionListNotifier)

View File

@@ -11,13 +11,16 @@ class AbuseReportService {
AbuseReportService(this.ref);
Future<SnAbuseReport> getReport(String id) async {
final response =
await ref.read(apiClientProvider).get('/safety/reports/me/$id');
final response = await ref
.read(apiClientProvider)
.get('/id/safety/reports/me/$id');
return SnAbuseReport.fromJson(response.data);
}
Future<List<SnAbuseReport>> getReports() async {
final response = await ref.read(apiClientProvider).get('/safety/reports/me');
final response = await ref
.read(apiClientProvider)
.get('/id/safety/reports/me');
return (response.data as List)
.map((json) => SnAbuseReport.fromJson(json))
.toList();

View File

@@ -67,7 +67,8 @@ class FortuneGraphWidget extends HookConsumerWidget {
constraints: const BoxConstraints(),
onPressed: () {
context.pushNamed(
'/account/$eventCalanderUser/calendar',
'accountCalendar',
pathParameters: {'name': eventCalanderUser!},
);
},
),

View File

@@ -38,7 +38,8 @@ class CloudFileList extends HookConsumerWidget {
double calculateAspectRatio() {
double total = 0;
for (var ratio in files.map((e) => e.fileMeta?['ratio'] ?? 1)) {
total += double.parse(ratio);
if (ratio is double) total += ratio;
if (ratio is String) total += double.parse(ratio);
}
if (total == 0) return 1;
return total / files.length;
@@ -78,6 +79,7 @@ class CloudFileList extends HookConsumerWidget {
if (!disableZoomIn) {
context.pushTransparentRoute(
CloudFileZoomIn(item: files.first, heroTag: heroTags.first),
rootNavigator: true,
);
}
},
@@ -505,7 +507,7 @@ class _CloudFileListEntry extends StatelessWidget {
if (isImage)
Positioned.fill(
child:
file.fileMeta?['blur'] != null
file.fileMeta?['blur'] is String
? BlurHash(hash: file.fileMeta?['blur'])
: ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),

View File

@@ -30,7 +30,9 @@ class CloudFileWidget extends ConsumerWidget {
var ratio =
item.fileMeta?['ratio'] is num
? item.fileMeta!['ratio'].toDouble()
: double.parse(item.fileMeta?['ratio'] ?? 1);
: item.fileMeta?['ratio'] is String
? double.parse(item.fileMeta!['ratio'])
: 1.0;
if (ratio == 0) ratio = 1.0;
final content = switch (item.mimeType?.split('/').firstOrNull) {
"image" => AspectRatio(

View File

@@ -72,7 +72,10 @@ class PostItem extends HookConsumerWidget {
children: [
GestureDetector(
onTap: () {
context.pushNamed('publisherProfile', pathParameters: {'name': item.publisher.name});
context.pushNamed(
'publisherProfile',
pathParameters: {'name': item.publisher.name},
);
},
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
@@ -254,7 +257,10 @@ class PostItem extends HookConsumerWidget {
GestureDetector(
child: ProfilePictureWidget(file: item.publisher.picture),
onTap: () {
context.pushNamed('publisherProfile', pathParameters: {'name': item.publisher.name});
context.pushNamed(
'publisherProfile',
pathParameters: {'name': item.publisher.name},
);
},
),
Expanded(
@@ -427,7 +433,10 @@ class PostItem extends HookConsumerWidget {
),
onTap: () {
if (isOpenable) {
context.pushNamed('postDetail', pathParameters: {'id': item.id});
context.pushNamed(
'postDetail',
pathParameters: {'id': item.id},
);
}
},
),
@@ -496,11 +505,13 @@ class PostItem extends HookConsumerWidget {
title: 'edit'.tr(),
image: MenuImage.icon(Symbols.edit),
callback: () {
context.pushNamed('postEdit', pathParameters: {'id': item.id}).then((value) {
if (value != null) {
onRefresh?.call();
}
});
context
.pushNamed('postEdit', pathParameters: {'id': item.id})
.then((value) {
if (value != null) {
onRefresh?.call();
}
});
},
),
if (isAuthor)
@@ -732,7 +743,13 @@ Widget _buildReferencePost(BuildContext context, SnPost item) {
),
],
),
).gestures(onTap: () => context.pushNamed('postDetail', pathParameters: {'id': referencePost.id}));
).gestures(
onTap:
() => context.pushNamed(
'postDetail',
pathParameters: {'id': referencePost.id},
),
);
}
class PostReactionList extends HookConsumerWidget {
@@ -757,7 +774,7 @@ class PostReactionList extends HookConsumerWidget {
submitting.value = true;
await client
.post(
'/posts/$parentId/reactions',
'/sphere/posts/$parentId/reactions',
data: {'symbol': symbol, 'attitude': attitude},
)
.catchError((err) {

View File

@@ -43,7 +43,7 @@ class PostQuickReply extends HookConsumerWidget {
try {
final client = ref.watch(apiClientProvider);
await client.post(
'/posts',
'/sphere/posts',
data: {
'content': contentController.text,
'replied_post_id': parent.id,

View File

@@ -35,7 +35,7 @@ class PostRepliesNotifier extends _$PostRepliesNotifier
final offset = cursor == null ? 0 : int.parse(cursor);
final response = await client.get(
'/posts/$_postId/replies',
'/sphere/posts/$_postId/replies',
queryParameters: {'offset': offset, 'take': _pageSize},
);
@@ -57,7 +57,11 @@ class PostRepliesNotifier extends _$PostRepliesNotifier
class PostRepliesList extends HookConsumerWidget {
final String postId;
final Color? backgroundColor;
const PostRepliesList({super.key, required this.postId, this.backgroundColor});
const PostRepliesList({
super.key,
required this.postId,
this.backgroundColor,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -93,7 +97,8 @@ class PostRepliesList extends HookConsumerWidget {
children: [
PostItem(
item: data.items[index],
backgroundColor: backgroundColor ?? (isWide ? Colors.transparent : null),
backgroundColor:
backgroundColor ?? (isWide ? Colors.transparent : null),
showReferencePost: false,
),
const Divider(height: 1),

View File

@@ -7,7 +7,7 @@ part of 'post_replies.dart';
// **************************************************************************
String _$postRepliesNotifierHash() =>
r'49c178102ec0a4136974a0e9a8f090f511abd542';
r'1cdda919249e3bf34459369e033ad5de8dbcf3f8';
/// Copied from Dart SDK
class _SystemHash {

View File

@@ -6,7 +6,7 @@ part of 'realm_list.dart';
// RiverpodGenerator
// **************************************************************************
String _$realmListNotifierHash() => r'02dee373a5609a5617b04ffec395d09dea7ae070';
String _$realmListNotifierHash() => r'8ae5c3ae2837acae4c7bf5e44578518afc9ea1a6';
/// Copied from Dart SDK
class _SystemHash {

View File

@@ -43,7 +43,7 @@ class AbuseReportSheet extends HookConsumerWidget {
try {
final client = ref.read(apiClientProvider);
await client.post(
'/safety/reports',
'/id/safety/reports',
data: {
'resource_identifier': resourceIdentifier,
'type': selectedType.value,